From 975f66f2eebe9dadba04f275774d4ab83f74cf25 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 14:04:41 +0200 Subject: Adding upstream version 7.7.0+dfsg. Signed-off-by: Daniel Baumann --- .../community/general/tests/.gitignore | 5 + .../community/general/tests/config.yml | 9 + .../general/tests/integration/requirements.yml | 9 + .../tests/integration/targets/aix_devices/aliases | 6 + .../integration/targets/aix_devices/tasks/main.yml | 81 + .../integration/targets/aix_filesystem/aliases | 5 + .../targets/aix_filesystem/tasks/main.yml | 130 + .../integration/targets/alerta_customer/aliases | 6 + .../targets/alerta_customer/defaults/main.yml | 9 + .../targets/alerta_customer/tasks/main.yml | 156 + .../tests/integration/targets/alternatives/aliases | 11 + .../targets/alternatives/tasks/main.yml | 93 + .../targets/alternatives/tasks/path_is_checked.yml | 17 + .../targets/alternatives/tasks/remove_links.yml | 13 + .../targets/alternatives/tasks/setup.yml | 19 + .../targets/alternatives/tasks/setup_test.yml | 16 + .../targets/alternatives/tasks/subcommands.yml | 222 + .../targets/alternatives/tasks/test.yml | 56 + .../targets/alternatives/tasks/tests.yml | 20 + .../alternatives/tasks/tests_set_priority.yml | 54 + .../targets/alternatives/tasks/tests_state.yml | 120 + .../alternatives/templates/dummy_alternative | 17 + .../targets/alternatives/templates/dummy_command | 6 + .../targets/alternatives/vars/Debian.yml | 7 + .../targets/alternatives/vars/Suse-42.3.yml | 7 + .../targets/alternatives/vars/default.yml | 7 + .../targets/ansible_galaxy_install/aliases | 8 + .../targets/ansible_galaxy_install/files/test.yml | 15 + .../targets/ansible_galaxy_install/meta/main.yml | 7 + .../targets/ansible_galaxy_install/tasks/main.yml | 88 + .../integration/targets/apache2_module/aliases | 7 + .../tasks/635-apache2-misleading-warning.yml | 47 + .../targets/apache2_module/tasks/actualtest.yml | 207 + .../targets/apache2_module/tasks/main.yml | 52 + .../tests/integration/targets/archive/aliases | 9 + .../integration/targets/archive/files/bar.txt | 5 + .../integration/targets/archive/files/empty.txt | 0 .../integration/targets/archive/files/foo.txt | 5 + .../targets/archive/files/sub/subfile.txt | 0 .../integration/targets/archive/meta/main.yml | 8 + .../integration/targets/archive/tasks/main.yml | 145 + .../targets/archive/tests/broken-link.yml | 35 + .../integration/targets/archive/tests/core.yml | 177 + .../targets/archive/tests/exclusions.yml | 44 + .../targets/archive/tests/idempotency.yml | 144 + .../integration/targets/archive/tests/remove.yml | 211 + .../integration/targets/btrfs_subvolume/aliases | 12 + .../targets/btrfs_subvolume/defaults/main.yml | 20 + .../targets/btrfs_subvolume/tasks/main.yml | 29 + .../btrfs_subvolume/tasks/run_common_tests.yml | 15 + .../btrfs_subvolume/tasks/run_filesystem_tests.yml | 32 + .../targets/btrfs_subvolume/tasks/setup.yml | 37 + .../tasks/test_filesystem_matching.yml | 80 + .../tasks/test_snapshot_clobber.yml | 41 + .../btrfs_subvolume/tasks/test_snapshot_error.yml | 42 + .../btrfs_subvolume/tasks/test_snapshot_skip.yml | 41 + .../tasks/test_subvolume_default.yml | 99 + .../tasks/test_subvolume_nested.yml | 61 + .../tasks/test_subvolume_recursive.yml | 86 + .../tasks/test_subvolume_simple.yml | 54 + .../tasks/test_subvolume_whitespace.yml | 62 + .../integration/targets/callback/inventory.yml | 9 + .../integration/targets/callback/tasks/main.yml | 100 + .../tests/integration/targets/callback_diy/aliases | 6 + .../targets/callback_diy/tasks/main.yml | 462 +++ .../integration/targets/callback_log_plays/aliases | 5 + .../targets/callback_log_plays/ping_log.yml | 9 + .../targets/callback_log_plays/runme.sh | 21 + .../integration/targets/callback_yaml/aliases | 6 + .../targets/callback_yaml/tasks/main.yml | 101 + .../tests/integration/targets/cargo/aliases | 7 + .../tests/integration/targets/cargo/meta/main.yml | 7 + .../tests/integration/targets/cargo/tasks/main.yml | 20 + .../integration/targets/cargo/tasks/setup.yml | 28 + .../targets/cargo/tasks/test_general.yml | 35 + .../targets/cargo/tasks/test_version.yml | 50 + .../targets/cloud_init_data_facts/aliases | 10 + .../targets/cloud_init_data_facts/meta/main.yml | 7 + .../targets/cloud_init_data_facts/tasks/main.yml | 68 + .../tests/integration/targets/cmd_runner/aliases | 5 + .../targets/cmd_runner/library/cmd_echo.py | 55 + .../integration/targets/cmd_runner/tasks/main.yml | 8 + .../targets/cmd_runner/tasks/test_cmd_echo.yml | 19 + .../integration/targets/cmd_runner/vars/main.yml | 123 + .../tests/integration/targets/connection/aliases | 5 + .../tests/integration/targets/connection/test.sh | 16 + .../targets/connection/test_connection.yml | 48 + .../integration/targets/connection_chroot/aliases | 7 + .../integration/targets/connection_chroot/runme.sh | 21 + .../connection_chroot/test_connection.inventory | 11 + .../integration/targets/connection_jail/aliases | 5 + .../integration/targets/connection_jail/runme.sh | 21 + .../connection_jail/test_connection.inventory | 11 + .../integration/targets/connection_lxc/aliases | 5 + .../integration/targets/connection_lxc/runme.sh | 21 + .../connection_lxc/test_connection.inventory | 21 + .../integration/targets/connection_lxd/aliases | 6 + .../integration/targets/connection_lxd/runme.sh | 21 + .../connection_lxd/test_connection.inventory | 10 + .../integration/targets/connection_posix/aliases | 6 + .../integration/targets/connection_posix/test.sh | 21 + .../tests/integration/targets/consul/aliases | 8 + .../tests/integration/targets/consul/meta/main.yml | 10 + .../targets/consul/tasks/consul_session.yml | 177 + .../integration/targets/consul/tasks/main.yml | 89 + .../targets/consul/templates/consul_config.hcl.j2 | 14 + .../general/tests/integration/targets/copr/aliases | 9 + .../tests/integration/targets/copr/tasks/main.yml | 160 + .../tests/integration/targets/copr/vars/main.yml | 15 + .../tests/integration/targets/cpanm/aliases | 10 + .../tests/integration/targets/cpanm/meta/main.yml | 7 + .../tests/integration/targets/cpanm/tasks/main.yml | 65 + .../tests/integration/targets/cronvar/aliases | 9 + .../integration/targets/cronvar/defaults/main.yml | 6 + .../integration/targets/cronvar/meta/main.yml | 7 + .../integration/targets/cronvar/tasks/main.yml | 124 + .../integration/targets/deploy_helper/aliases | 5 + .../targets/deploy_helper/meta/main.yml | 7 + .../targets/deploy_helper/tasks/main.yml | 158 + .../tests/integration/targets/discord/README.md | 20 + .../tests/integration/targets/discord/aliases | 5 + .../integration/targets/discord/defaults/main.yml | 7 + .../integration/targets/discord/tasks/main.yml | 69 + .../integration/targets/django_manage/aliases | 15 + .../single_app_project/core/settings.py | 6 + .../single_app_project/manage.py | 21 + .../files/base_test/simple_project/p1/manage.py | 29 + .../base_test/simple_project/p1/p1/settings.py | 133 + .../files/base_test/simple_project/p1/p1/urls.py | 28 + .../django_manage/files/base_test/startproj/.keep | 0 .../targets/django_manage/meta/main.yml | 7 + .../targets/django_manage/tasks/main.yaml | 84 + .../integration/targets/dnf_versionlock/aliases | 9 + .../targets/dnf_versionlock/tasks/install.yml | 10 + .../targets/dnf_versionlock/tasks/lock_bash.yml | 36 + .../targets/dnf_versionlock/tasks/lock_updates.yml | 74 + .../targets/dnf_versionlock/tasks/main.yml | 12 + .../tests/integration/targets/dpkg_divert/aliases | 10 + .../integration/targets/dpkg_divert/tasks/main.yml | 13 + .../targets/dpkg_divert/tasks/prepare.yml | 43 + .../targets/dpkg_divert/tasks/tests/01-basic.yml | 291 ++ .../targets/dpkg_divert/tasks/tests/02-rename.yml | 384 ++ .../tests/integration/targets/etcd3/aliases | 12 + .../tests/integration/targets/etcd3/meta/main.yml | 8 + .../tests/integration/targets/etcd3/tasks/main.yml | 18 + .../integration/targets/etcd3/tasks/run_tests.yml | 81 + .../tests/integration/targets/filesize/aliases | 6 + .../integration/targets/filesize/defaults/main.yml | 8 + .../integration/targets/filesize/tasks/basics.yml | 411 ++ .../integration/targets/filesize/tasks/errors.yml | 133 + .../integration/targets/filesize/tasks/floats.yml | 249 ++ .../integration/targets/filesize/tasks/main.yml | 44 + .../integration/targets/filesize/tasks/sparse.yml | 286 ++ .../targets/filesize/tasks/symlinks.yml | 97 + .../tests/integration/targets/filesystem/aliases | 10 + .../targets/filesystem/defaults/main.yml | 35 + .../integration/targets/filesystem/meta/main.yml | 8 + .../targets/filesystem/tasks/create_device.yml | 64 + .../targets/filesystem/tasks/create_fs.yml | 119 + .../targets/filesystem/tasks/freebsd_setup.yml | 14 + .../integration/targets/filesystem/tasks/main.yml | 107 + .../filesystem/tasks/overwrite_another_fs.yml | 59 + .../targets/filesystem/tasks/remove_fs.yml | 102 + .../integration/targets/filesystem/tasks/setup.yml | 154 + .../targets/filesystem/vars/Ubuntu-14.04.yml | 7 + .../targets/filesystem/vars/default.yml | 6 + .../integration/targets/filter_counter/aliases | 6 + .../targets/filter_counter/tasks/main.yml | 41 + .../tests/integration/targets/filter_dict/aliases | 6 + .../integration/targets/filter_dict/tasks/main.yml | 11 + .../integration/targets/filter_dict_kv/aliases | 6 + .../targets/filter_dict_kv/tasks/main.yml | 14 + .../integration/targets/filter_from_csv/aliases | 6 + .../targets/filter_from_csv/tasks/main.yml | 54 + .../targets/filter_from_csv/vars/main.yml | 31 + .../targets/filter_groupby_as_dict/aliases | 6 + .../targets/filter_groupby_as_dict/tasks/main.yml | 49 + .../targets/filter_groupby_as_dict/vars/main.yml | 35 + .../integration/targets/filter_hashids/aliases | 6 + .../integration/targets/filter_hashids/runme.sh | 15 + .../integration/targets/filter_hashids/runme.yml | 8 + .../targets/filter_hashids/tasks/main.yml | 63 + .../targets/filter_hashids/vars/main.yml | 9 + .../tests/integration/targets/filter_jc/aliases | 7 + .../tests/integration/targets/filter_jc/runme.sh | 15 + .../tests/integration/targets/filter_jc/runme.yml | 8 + .../integration/targets/filter_jc/tasks/main.yml | 14 + .../integration/targets/filter_json_query/aliases | 7 + .../integration/targets/filter_json_query/runme.sh | 15 + .../targets/filter_json_query/runme.yml | 8 + .../targets/filter_json_query/tasks/main.yml | 14 + .../targets/filter_json_query/vars/main.yml | 16 + .../targets/filter_lists_mergeby/aliases | 6 + .../tasks/lists_mergeby_2-10.yml | 143 + .../tasks/lists_mergeby_default.yml | 169 + .../targets/filter_lists_mergeby/tasks/main.yml | 11 + .../targets/filter_lists_mergeby/vars/main.yml | 209 + .../targets/filter_path_join_shim/aliases | 6 + .../targets/filter_path_join_shim/tasks/main.yml | 11 + .../integration/targets/filter_random_mac/aliases | 7 + .../targets/filter_random_mac/meta/main.yml | 7 + .../targets/filter_random_mac/tasks/main.yml | 62 + .../tests/integration/targets/filter_time/aliases | 6 + .../integration/targets/filter_time/tasks/main.yml | 115 + .../targets/filter_unicode_normalize/aliases | 6 + .../filter_unicode_normalize/tasks/main.yml | 44 + .../targets/filter_unicode_normalize/vars/main.yml | 9 + .../targets/filter_version_sort/aliases | 6 + .../targets/filter_version_sort/tasks/main.yml | 14 + .../tests/integration/targets/flatpak/aliases | 12 + .../integration/targets/flatpak/files/serve.py | 69 + .../integration/targets/flatpak/meta/main.yml | 7 + .../targets/flatpak/tasks/check_mode.yml | 197 + .../integration/targets/flatpak/tasks/main.yml | 64 + .../integration/targets/flatpak/tasks/setup.yml | 68 + .../integration/targets/flatpak/tasks/test.yml | 289 ++ .../integration/targets/flatpak_remote/aliases | 12 + .../targets/flatpak_remote/meta/main.yml | 7 + .../targets/flatpak_remote/tasks/check_mode.yml | 206 + .../targets/flatpak_remote/tasks/main.yml | 50 + .../targets/flatpak_remote/tasks/setup.yml | 40 + .../targets/flatpak_remote/tasks/test.yml | 135 + .../integration/targets/gandi_livedns/aliases | 6 + .../targets/gandi_livedns/defaults/main.yml | 37 + .../targets/gandi_livedns/tasks/create_record.yml | 69 + .../targets/gandi_livedns/tasks/main.yml | 7 + .../targets/gandi_livedns/tasks/record.yml | 8 + .../targets/gandi_livedns/tasks/remove_record.yml | 61 + .../targets/gandi_livedns/tasks/update_record.yml | 59 + .../general/tests/integration/targets/gem/aliases | 9 + .../tests/integration/targets/gem/meta/main.yml | 8 + .../tests/integration/targets/gem/tasks/main.yml | 213 + .../tests/integration/targets/gem/vars/FreeBSD.yml | 8 + .../tests/integration/targets/gem/vars/RedHat.yml | 7 + .../tests/integration/targets/gem/vars/default.yml | 6 + .../tests/integration/targets/git_config/aliases | 7 + .../integration/targets/git_config/files/gitconfig | 6 + .../integration/targets/git_config/meta/main.yml | 7 + .../git_config/tasks/exclusion_state_list-all.yml | 20 + .../targets/git_config/tasks/get_set_no_state.yml | 29 + .../git_config/tasks/get_set_state_present.yml | 31 + .../tasks/get_set_state_present_file.yml | 32 + .../integration/targets/git_config/tasks/main.yml | 35 + .../tasks/precedence_between_unset_and_value.yml | 29 + .../git_config/tasks/set_value_with_tilde.yml | 37 + .../integration/targets/git_config/tasks/setup.yml | 15 + .../targets/git_config/tasks/setup_no_value.yml | 16 + .../targets/git_config/tasks/setup_value.yml | 16 + .../targets/git_config/tasks/unset_check_mode.yml | 29 + .../targets/git_config/tasks/unset_no_value.yml | 27 + .../targets/git_config/tasks/unset_value.yml | 28 + .../integration/targets/git_config/vars/main.yml | 10 + .../tests/integration/targets/github_issue/aliases | 6 + .../targets/github_issue/tasks/main.yml | 38 + .../integration/targets/github_issue/vars/main.yml | 9 + .../integration/targets/gitlab_branch/aliases | 6 + .../targets/gitlab_branch/defaults/main.yml | 7 + .../targets/gitlab_branch/tasks/main.yml | 69 + .../integration/targets/gitlab_deploy_key/aliases | 7 + .../targets/gitlab_deploy_key/defaults/main.yml | 8 + .../targets/gitlab_deploy_key/tasks/main.yml | 78 + .../tests/integration/targets/gitlab_group/aliases | 7 + .../targets/gitlab_group/defaults/main.yml | 6 + .../targets/gitlab_group/tasks/main.yml | 129 + .../targets/gitlab_group_members/aliases | 5 + .../targets/gitlab_group_members/tasks/main.yml | 74 + .../targets/gitlab_group_members/vars/main.yml | 18 + .../targets/gitlab_group_variable/aliases | 5 + .../targets/gitlab_group_variable/tasks/main.yml | 706 ++++ .../tests/integration/targets/gitlab_hook/aliases | 7 + .../targets/gitlab_hook/defaults/main.yml | 7 + .../integration/targets/gitlab_hook/tasks/main.yml | 77 + .../integration/targets/gitlab_project/aliases | 7 + .../targets/gitlab_project/defaults/main.yml | 7 + .../targets/gitlab_project/tasks/main.yml | 49 + .../targets/gitlab_project_badge/aliases | 6 + .../targets/gitlab_project_badge/defaults/main.yml | 11 + .../targets/gitlab_project_badge/tasks/main.yml | 214 + .../targets/gitlab_project_members/aliases | 5 + .../gitlab_project_members/defaults/main.yml | 18 + .../targets/gitlab_project_members/tasks/main.yml | 124 + .../targets/gitlab_project_variable/aliases | 5 + .../targets/gitlab_project_variable/tasks/main.yml | 701 ++++ .../integration/targets/gitlab_runner/aliases | 7 + .../targets/gitlab_runner/defaults/main.yml | 8 + .../targets/gitlab_runner/tasks/main.yml | 78 + .../tests/integration/targets/gitlab_user/aliases | 7 + .../targets/gitlab_user/defaults/main.yml | 11 + .../integration/targets/gitlab_user/tasks/main.yml | 257 ++ .../targets/gitlab_user/tasks/sshkey.yml | 139 + .../general/tests/integration/targets/hg/aliases | 7 + .../tests/integration/targets/hg/meta/main.yml | 8 + .../tests/integration/targets/hg/tasks/install.yml | 88 + .../tests/integration/targets/hg/tasks/main.yml | 45 + .../integration/targets/hg/tasks/run-tests.yml | 85 + .../integration/targets/hg/tasks/uninstall.yml | 53 + .../tests/integration/targets/homebrew/aliases | 10 + .../integration/targets/homebrew/tasks/main.yml | 99 + .../integration/targets/homebrew_cask/aliases | 10 + .../targets/homebrew_cask/defaults/main.yml | 6 + .../targets/homebrew_cask/tasks/main.yml | 73 + .../tests/integration/targets/homectl/aliases | 11 + .../integration/targets/homectl/tasks/main.yml | 182 + .../integration/targets/hwc_ecs_instance/aliases | 5 + .../targets/hwc_ecs_instance/tasks/main.yml | 319 ++ .../tests/integration/targets/hwc_evs_disk/aliases | 5 + .../targets/hwc_evs_disk/tasks/main.yml | 113 + .../integration/targets/hwc_network_vpc/aliases | 5 + .../targets/hwc_network_vpc/tasks/main.yml | 105 + .../integration/targets/hwc_smn_topic/aliases | 5 + .../targets/hwc_smn_topic/tasks/main.yml | 86 + .../tests/integration/targets/hwc_vpc_eip/aliases | 5 + .../integration/targets/hwc_vpc_eip/tasks/main.yml | 190 + .../targets/hwc_vpc_peering_connect/aliases | 5 + .../targets/hwc_vpc_peering_connect/tasks/main.yml | 155 + .../tests/integration/targets/hwc_vpc_port/aliases | 5 + .../targets/hwc_vpc_port/tasks/main.yml | 141 + .../integration/targets/hwc_vpc_private_ip/aliases | 5 + .../targets/hwc_vpc_private_ip/tasks/main.yml | 142 + .../integration/targets/hwc_vpc_route/aliases | 5 + .../targets/hwc_vpc_route/tasks/main.yml | 159 + .../targets/hwc_vpc_security_group/aliases | 5 + .../targets/hwc_vpc_security_group/tasks/main.yml | 91 + .../targets/hwc_vpc_security_group_rule/aliases | 5 + .../hwc_vpc_security_group_rule/tasks/main.yml | 166 + .../integration/targets/hwc_vpc_subnet/aliases | 5 + .../targets/hwc_vpc_subnet/tasks/main.yml | 152 + .../targets/ilo_redfish_command/aliases | 5 + .../targets/ilo_redfish_command/tasks/main.yml | 12 + .../integration/targets/ilo_redfish_config/aliases | 5 + .../targets/ilo_redfish_config/tasks/main.yml | 53 + .../integration/targets/ilo_redfish_info/aliases | 5 + .../targets/ilo_redfish_info/tasks/main.yml | 13 + .../integration/targets/influxdb_user/aliases | 11 + .../targets/influxdb_user/meta/main.yml | 7 + .../targets/influxdb_user/tasks/main.yml | 12 + .../targets/influxdb_user/tasks/tests.yml | 143 + .../tests/integration/targets/ini_file/aliases | 5 + .../integration/targets/ini_file/meta/main.yml | 7 + .../integration/targets/ini_file/tasks/main.yml | 40 + .../targets/ini_file/tasks/tests/00-basic.yml | 42 + .../targets/ini_file/tasks/tests/01-value.yml | 592 +++ .../targets/ini_file/tasks/tests/02-values.yml | 1023 +++++ .../targets/ini_file/tasks/tests/03-encoding.yml | 44 + .../integration/targets/interfaces_file/aliases | 5 + .../targets/interfaces_file/files/interfaces_ff | 11 + .../interfaces_file/files/interfaces_ff_3841 | 11 + .../targets/interfaces_file/meta/main.yml | 7 + .../targets/interfaces_file/tasks/main.yml | 67 + .../tests/integration/targets/ipify_facts/aliases | 5 + .../integration/targets/ipify_facts/tasks/main.yml | 33 + .../integration/targets/ipify_facts/vars/main.yml | 6 + .../integration/targets/iptables_state/aliases | 12 + .../targets/iptables_state/meta/main.yml | 7 + .../targets/iptables_state/tasks/main.yml | 38 + .../iptables_state/tasks/tests/00-basic.yml | 320 ++ .../iptables_state/tasks/tests/01-tables.yml | 294 ++ .../iptables_state/tasks/tests/10-rollback.yml | 203 + .../tests/integration/targets/ipwcli_dns/aliases | 6 + .../integration/targets/ipwcli_dns/tasks/main.yml | 115 + .../tests/integration/targets/iso_create/aliases | 8 + .../integration/targets/iso_create/files/test1.cfg | 61 + .../targets/iso_create/files/test_dir/test2.cfg | 61 + .../integration/targets/iso_create/meta/main.yml | 9 + .../integration/targets/iso_create/tasks/main.yml | 163 + .../targets/iso_create/tasks/prepare_dest_dir.yml | 13 + .../integration/targets/iso_customize/aliases | 13 + .../targets/iso_customize/meta/main.yml | 8 + .../targets/iso_customize/tasks/iso_customize.yml | 75 + .../tasks/iso_customize_add_files.yml | 34 + .../tasks/iso_customize_delete_files.yml | 34 + .../tasks/iso_customize_exception.yml | 71 + .../targets/iso_customize/tasks/iso_mount.yml | 39 + .../targets/iso_customize/tasks/main.yml | 94 + .../targets/iso_customize/tasks/prepare.yml | 40 + .../tests/integration/targets/iso_extract/aliases | 13 + .../integration/targets/iso_extract/files/test.iso | Bin 0 -> 374784 bytes .../targets/iso_extract/files/test.iso.license | 3 + .../integration/targets/iso_extract/meta/main.yml | 8 + .../integration/targets/iso_extract/tasks/7zip.yml | 54 + .../integration/targets/iso_extract/tasks/main.yml | 43 + .../targets/iso_extract/tasks/prepare.yml | 21 + .../targets/iso_extract/tasks/tests.yml | 40 + .../targets/iso_extract/vars/Alpine.yml | 6 + .../targets/iso_extract/vars/Archlinux.yml | 6 + .../targets/iso_extract/vars/Debian.yml | 6 + .../targets/iso_extract/vars/FreeBSD.yml | 6 + .../targets/iso_extract/vars/RedHat.yml | 6 + .../integration/targets/iso_extract/vars/Suse.yml | 8 + .../targets/iso_extract/vars/Ubuntu.yml | 6 + .../targets/iso_extract/vars/default.yml | 4 + .../tests/integration/targets/java_cert/aliases | 11 + .../targets/java_cert/defaults/main.yml | 19 + .../targets/java_cert/files/setupSSLServer.py | 24 + .../targets/java_cert/files/testpkcs.p12 | Bin 0 -> 2532 bytes .../targets/java_cert/files/testpkcs.p12.license | 3 + .../integration/targets/java_cert/meta/main.yml | 9 + .../integration/targets/java_cert/tasks/main.yml | 116 + .../targets/java_cert/tasks/state_change.yml | 298 ++ .../integration/targets/java_keystore/aliases | 11 + .../targets/java_keystore/defaults/main.yml | 20 + .../targets/java_keystore/meta/main.yml | 9 + .../targets/java_keystore/tasks/main.yml | 43 + .../targets/java_keystore/tasks/prepare.yml | 37 + .../targets/java_keystore/tasks/tests.yml | 313 ++ .../tests/integration/targets/jboss/aliases | 12 + .../tests/integration/targets/jboss/meta/main.yml | 7 + .../integration/targets/jboss/tasks/jboss.yml | 238 ++ .../tests/integration/targets/jboss/tasks/main.yml | 11 + .../general/tests/integration/targets/jira/aliases | 6 + .../tests/integration/targets/jira/tasks/main.yml | 111 + .../tests/integration/targets/jira/vars/main.yml | 11 + .../tests/integration/targets/kdeconfig/aliases | 5 + .../integration/targets/kdeconfig/meta/main.yml | 7 + .../targets/kdeconfig/tasks/files/kwriteconf_fake | 38 + .../integration/targets/kdeconfig/tasks/main.yml | 369 ++ .../integration/targets/kernel_blacklist/aliases | 5 + .../targets/kernel_blacklist/files/blacklist | 7 + .../targets/kernel_blacklist/meta/main.yml | 7 + .../targets/kernel_blacklist/tasks/main.yml | 111 + .../keycloak_authz_authorization_scope/aliases | 5 + .../keycloak_authz_authorization_scope/readme.adoc | 27 + .../tasks/main.yml | 234 ++ .../vars/main.yml | 11 + .../integration/targets/keycloak_client/README.md | 17 + .../targets/keycloak_client/docker-compose.yml | 31 + .../targets/keycloak_client/tasks/main.yml | 63 + .../targets/keycloak_client/vars/main.yml | 61 + .../targets/keycloak_clientscope_type/README.md | 16 + .../keycloak_clientscope_type/docker-compose.yml | 16 + .../keycloak_clientscope_type/tasks/main.yml | 164 + .../keycloak_clientscope_type/vars/main.yml | 11 + .../targets/keycloak_clientsecret_info/README.md | 17 + .../keycloak_clientsecret_info/docker-compose.yml | 31 + .../keycloak_clientsecret_info/tasks/main.yml | 48 + .../keycloak_clientsecret_info/vars/main.yml | 20 + .../keycloak_clientsecret_regenerate/README.md | 17 + .../docker-compose.yml | 31 + .../tasks/main.yml | 49 + .../keycloak_clientsecret_regenerate/vars/main.yml | 20 + .../integration/targets/keycloak_group/aliases | 5 + .../targets/keycloak_group/meta/main.yml | 7 + .../integration/targets/keycloak_group/readme.adoc | 27 + .../targets/keycloak_group/tasks/main.yml | 527 +++ .../targets/keycloak_group/vars/main.yml | 10 + .../targets/keycloak_identity_provider/aliases | 5 + .../keycloak_identity_provider/tasks/main.yml | 175 + .../keycloak_identity_provider/vars/main.yml | 11 + .../integration/targets/keycloak_role/aliases | 5 + .../targets/keycloak_role/tasks/main.yml | 250 ++ .../targets/keycloak_role/vars/main.yml | 14 + .../targets/keycloak_user_federation/aliases | 5 + .../keycloak_user_federation/tasks/main.yml | 425 ++ .../targets/keycloak_user_federation/vars/main.yml | 11 + .../targets/keycloak_user_rolemapping/aliases | 4 + .../keycloak_user_rolemapping/tasks/main.yml | 143 + .../keycloak_user_rolemapping/vars/main.yml | 14 + .../tests/integration/targets/keyring/aliases | 5 + .../integration/targets/keyring/tasks/main.yml | 99 + .../integration/targets/keyring/vars/main.yml | 7 + .../tests/integration/targets/launchd/aliases | 7 + .../targets/launchd/files/ansible_test_service.py | 24 + .../integration/targets/launchd/tasks/main.yml | 30 + .../integration/targets/launchd/tasks/setup.yml | 23 + .../integration/targets/launchd/tasks/teardown.yml | 30 + .../integration/targets/launchd/tasks/test.yml | 11 + .../targets/launchd/tasks/tests/test_reload.yml | 71 + .../targets/launchd/tasks/tests/test_restart.yml | 46 + .../targets/launchd/tasks/tests/test_runatload.yml | 36 + .../launchd/tasks/tests/test_start_stop.yml | 115 + .../targets/launchd/tasks/tests/test_unknown.yml | 14 + .../targets/launchd/tasks/tests/test_unload.yml | 65 + .../templates/launchd.test.service.plist.j2 | 18 + .../modified.launchd.test.service.plist.j2 | 18 + .../integration/targets/launchd/vars/main.yml | 7 + .../tests/integration/targets/ldap_search/aliases | 11 + .../integration/targets/ldap_search/meta/main.yml | 7 + .../integration/targets/ldap_search/tasks/main.yml | 16 + .../targets/ldap_search/tasks/tests/basic.yml | 25 + .../integration/targets/listen_ports_facts/aliases | 10 + .../targets/listen_ports_facts/meta/main.yml | 7 + .../targets/listen_ports_facts/tasks/main.yml | 112 + .../tests/integration/targets/locale_gen/aliases | 8 + .../targets/locale_gen/tasks/locale_gen.yml | 99 + .../integration/targets/locale_gen/tasks/main.yml | 12 + .../integration/targets/lookup_cartesian/aliases | 7 + .../targets/lookup_cartesian/tasks/main.yml | 32 + .../targets/lookup_collection_version/aliases | 5 + .../ansible_collections/testns/testcoll/galaxy.yml | 12 + .../testcoll/plugins/modules/collection_module.py | 33 + .../testns/testcoll_mf/FILES.json | 40 + .../testns/testcoll_mf/FILES.json.license | 3 + .../testns/testcoll_mf/MANIFEST.json | 30 + .../testns/testcoll_mf/MANIFEST.json.license | 3 + .../testns/testcoll_mf/README.md | 0 .../plugins/modules/collection_module.py | 33 + .../plugins/modules/collection_module.py | 33 + .../testns/testcoll_nv/galaxy.yml | 11 + .../plugins/modules/collection_module.py | 33 + .../library/local_module.py | 33 + .../targets/lookup_collection_version/runme.sh | 20 + .../targets/lookup_collection_version/runme.yml | 40 + .../integration/targets/lookup_dependent/aliases | 6 + .../targets/lookup_dependent/tasks/main.yml | 183 + .../tests/integration/targets/lookup_dig/aliases | 6 + .../integration/targets/lookup_dig/meta/main.yml | 7 + .../integration/targets/lookup_dig/tasks/main.yml | 37 + .../tests/integration/targets/lookup_etcd3/aliases | 14 + .../targets/lookup_etcd3/defaults/main.yml | 7 + .../targets/lookup_etcd3/dependencies.yml | 10 + .../integration/targets/lookup_etcd3/meta/main.yml | 7 + .../integration/targets/lookup_etcd3/runme.sh | 11 + .../targets/lookup_etcd3/tasks/main.yml | 28 + .../targets/lookup_etcd3/tasks/tests.yml | 27 + .../targets/lookup_etcd3/test_lookup_etcd3.yml | 10 + .../integration/targets/lookup_flattened/aliases | 7 + .../targets/lookup_flattened/tasks/main.yml | 24 + .../integration/targets/lookup_lmdb_kv/aliases | 8 + .../targets/lookup_lmdb_kv/dependencies.yml | 16 + .../integration/targets/lookup_lmdb_kv/runme.sh | 11 + .../integration/targets/lookup_lmdb_kv/test.yml | 31 + .../integration/targets/lookup_lmdb_kv/test_db.py | 15 + .../targets/lookup_merge_variables/aliases | 6 + .../targets/lookup_merge_variables/runme.sh | 13 + .../targets/lookup_merge_variables/test.yml | 174 + .../lookup_merge_variables/test_with_env.yml | 44 + .../targets/lookup_merge_variables/vars.yml | 34 + .../targets/lookup_passwordstore/aliases | 11 + .../targets/lookup_passwordstore/meta/main.yml | 7 + .../targets/lookup_passwordstore/tasks/main.yml | 17 + .../targets/lookup_passwordstore/tasks/package.yml | 84 + .../lookup_passwordstore/tasks/password_tests.yml | 130 + .../targets/lookup_passwordstore/tasks/tests.yml | 239 ++ .../targets/lookup_passwordstore/templates/input | 9 + .../lookup_passwordstore/templates/input.license | 3 + .../templates/security-privacy.repo.j2 | 13 + .../targets/lookup_passwordstore/vars/Alpine.yml | 8 + .../lookup_passwordstore/vars/Archlinux.yml | 8 + .../targets/lookup_passwordstore/vars/Debian.yml | 7 + .../targets/lookup_passwordstore/vars/Fedora.yml | 8 + .../targets/lookup_passwordstore/vars/FreeBSD.yml | 9 + .../targets/lookup_passwordstore/vars/default.yml | 4 + .../targets/lookup_passwordstore/vars/main.yml | 122 + .../integration/targets/lookup_random_pet/aliases | 7 + .../targets/lookup_random_pet/dependencies.yml | 10 + .../integration/targets/lookup_random_pet/runme.sh | 11 + .../integration/targets/lookup_random_pet/test.yml | 30 + .../targets/lookup_random_string/aliases | 7 + .../targets/lookup_random_string/runme.sh | 8 + .../targets/lookup_random_string/test.yml | 53 + .../targets/lookup_random_words/aliases | 7 + .../targets/lookup_random_words/dependencies.yml | 10 + .../targets/lookup_random_words/runme.sh | 11 + .../targets/lookup_random_words/test.yml | 32 + .../general/tests/integration/targets/lvg/aliases | 12 + .../tests/integration/targets/lvg/meta/main.yml | 8 + .../tests/integration/targets/lvg/tasks/main.yml | 27 + .../tests/integration/targets/lvg/tasks/setup.yml | 27 + .../integration/targets/lvg/tasks/teardown.yml | 23 + .../targets/lvg/tasks/test_grow_reduce.yml | 38 + .../targets/lvg/tasks/test_indempotency.yml | 20 + .../targets/lvg/tasks/test_pvresize.yml | 81 + .../tests/integration/targets/lxd_project/aliases | 5 + .../integration/targets/lxd_project/tasks/main.yml | 142 + .../general/tests/integration/targets/mail/aliases | 5 + .../integration/targets/mail/files/smtpserver.crt | 26 + .../integration/targets/mail/files/smtpserver.key | 32 + .../integration/targets/mail/files/smtpserver.py | 69 + .../tests/integration/targets/mail/meta/main.yml | 7 + .../tests/integration/targets/mail/tasks/main.yml | 105 + .../general/tests/integration/targets/mas/aliases | 6 + .../tests/integration/targets/mas/tasks/main.yml | 158 + .../integration/targets/memset_dns_reload/aliases | 5 + .../targets/memset_dns_reload/meta/main.yml | 4 + .../targets/memset_dns_reload/tasks/main.yml | 33 + .../targets/memset_memstore_info/aliases | 5 + .../targets/memset_memstore_info/meta/main.yml | 4 + .../targets/memset_memstore_info/tasks/main.yml | 34 + .../integration/targets/memset_server_info/aliases | 5 + .../targets/memset_server_info/meta/main.yml | 4 + .../targets/memset_server_info/tasks/main.yml | 34 + .../tests/integration/targets/memset_zone/aliases | 5 + .../integration/targets/memset_zone/meta/main.yml | 4 + .../integration/targets/memset_zone/tasks/main.yml | 125 + .../integration/targets/memset_zone/vars/main.yml | 6 + .../integration/targets/memset_zone_domain/aliases | 5 + .../targets/memset_zone_domain/meta/main.yml | 4 + .../targets/memset_zone_domain/tasks/main.yml | 152 + .../targets/memset_zone_domain/vars/main.yml | 8 + .../integration/targets/memset_zone_record/aliases | 5 + .../targets/memset_zone_record/meta/main.yml | 4 + .../targets/memset_zone_record/tasks/main.yml | 235 ++ .../targets/memset_zone_record/vars/main.yml | 7 + .../integration/targets/module_helper/aliases | 5 + .../targets/module_helper/library/mdepfail.py | 70 + .../targets/module_helper/library/msimple.py | 78 + .../targets/module_helper/library/msimpleda.py | 66 + .../targets/module_helper/library/mstate.py | 78 + .../targets/module_helper/tasks/main.yml | 9 + .../targets/module_helper/tasks/mdepfail.yml | 18 + .../targets/module_helper/tasks/msimple.yml | 85 + .../tasks/msimple_output_conflict.yml | 54 + .../targets/module_helper/tasks/msimpleda.yml | 39 + .../targets/module_helper/tasks/mstate.yml | 83 + .../tests/integration/targets/monit/aliases | 14 + .../integration/targets/monit/defaults/main.yml | 9 + .../integration/targets/monit/files/httpd_echo.py | 51 + .../tests/integration/targets/monit/meta/main.yml | 8 + .../targets/monit/tasks/check_state.yml | 21 + .../tests/integration/targets/monit/tasks/main.yml | 99 + .../tests/integration/targets/monit/tasks/test.yml | 33 + .../targets/monit/tasks/test_errors.yml | 11 + .../targets/monit/tasks/test_reload_present.yml | 65 + .../integration/targets/monit/tasks/test_state.yml | 38 + .../integration/targets/monit/templates/monitrc.j2 | 19 + .../integration/targets/monit/vars/Alpine.yml | 6 + .../integration/targets/monit/vars/Archlinux.yml | 6 + .../integration/targets/monit/vars/CentOS-6.yml | 6 + .../integration/targets/monit/vars/RedHat.yml | 6 + .../tests/integration/targets/monit/vars/Suse.yml | 6 + .../integration/targets/monit/vars/defaults.yml | 6 + .../general/tests/integration/targets/mqtt/aliases | 10 + .../tests/integration/targets/mqtt/meta/main.yml | 7 + .../tests/integration/targets/mqtt/tasks/main.yml | 14 + .../integration/targets/mqtt/tasks/ubuntu.yml | 147 + .../tests/integration/targets/mssql_script/aliases | 10 + .../targets/mssql_script/defaults/main.yml | 9 + .../integration/targets/mssql_script/meta/main.yml | 7 + .../targets/mssql_script/tasks/main.yml | 246 ++ .../tests/integration/targets/nomad/aliases | 10 + .../tests/integration/targets/nomad/files/job.hcl | 400 ++ .../tests/integration/targets/nomad/meta/main.yml | 10 + .../tests/integration/targets/nomad/tasks/main.yml | 111 + .../integration/targets/nomad/tasks/nomad_job.yml | 111 + .../general/tests/integration/targets/npm/aliases | 8 + .../tests/integration/targets/npm/meta/main.yml | 9 + .../tests/integration/targets/npm/tasks/main.yml | 32 + .../integration/targets/npm/tasks/no_bin_links.yml | 68 + .../tests/integration/targets/npm/tasks/run.yml | 8 + .../tests/integration/targets/npm/tasks/setup.yml | 11 + .../tests/integration/targets/npm/tasks/test.yml | 74 + .../general/tests/integration/targets/odbc/aliases | 12 + .../integration/targets/odbc/defaults/main.yml | 38 + .../tests/integration/targets/odbc/meta/main.yml | 8 + .../targets/odbc/tasks/install_pyodbc.yml | 12 + .../tests/integration/targets/odbc/tasks/main.yml | 158 + .../targets/odbc/tasks/negative_tests.yml | 24 + .../integration/targets/odbc/tasks/no_pyodbc.yml | 16 + .../tests/integration/targets/one_host/aliases | 7 + .../files/testhost/tmp/opennebula-fixtures.json.gz | Bin 0 -> 2950 bytes .../tmp/opennebula-fixtures.json.gz.license | 3 + .../integration/targets/one_host/meta/main.yml | 7 + .../integration/targets/one_host/tasks/main.yml | 243 ++ .../tests/integration/targets/one_template/aliases | 7 + .../files/testhost/tmp/opennebula-fixtures.json.gz | Bin 0 -> 1069 bytes .../tmp/opennebula-fixtures.json.gz.license | 3 + .../integration/targets/one_template/meta/main.yml | 7 + .../targets/one_template/tasks/main.yml | 246 ++ .../tests/integration/targets/osx_defaults/aliases | 9 + .../targets/osx_defaults/tasks/main.yml | 255 ++ .../tests/integration/targets/pacman/aliases | 12 + .../tests/integration/targets/pacman/meta/main.yml | 8 + .../integration/targets/pacman/tasks/basic.yml | 86 + .../pacman/tasks/locally_installed_package.yml | 85 + .../integration/targets/pacman/tasks/main.yml | 19 + .../targets/pacman/tasks/package_urls.yml | 219 + .../integration/targets/pacman/tasks/reason.yml | 101 + .../targets/pacman/tasks/remove_nosave.yml | 74 + .../targets/pacman/tasks/update_cache.yml | 27 + .../integration/targets/pagerduty_user/aliases | 5 + .../targets/pagerduty_user/tasks/main.yml | 25 + .../targets/pagerduty_user/vars/main.yml | 10 + .../tests/integration/targets/pam_limits/aliases | 9 + .../targets/pam_limits/files/test_pam_limits.conf | 5 + .../integration/targets/pam_limits/tasks/main.yml | 92 + .../general/tests/integration/targets/pamd/aliases | 9 + .../tests/integration/targets/pamd/tasks/main.yml | 74 + .../tests/integration/targets/parted/aliases | 13 + .../integration/targets/parted/handlers/main.yml | 14 + .../integration/targets/parted/tasks/main.yml | 86 + .../general/tests/integration/targets/pids/aliases | 5 + .../tests/integration/targets/pids/files/sleeper.c | 13 + .../tests/integration/targets/pids/meta/main.yml | 7 + .../tests/integration/targets/pids/tasks/main.yml | 120 + .../targets/pids/templates/obtainpid.sh | 7 + .../general/tests/integration/targets/pipx/aliases | 8 + .../tests/integration/targets/pipx/tasks/main.yml | 316 ++ .../tests/integration/targets/pipx_info/aliases | 8 + .../integration/targets/pipx_info/tasks/main.yml | 140 + .../tests/integration/targets/pkgng/aliases | 9 + .../targets/pkgng/tasks/create-outofdate-pkg.yml | 52 + .../integration/targets/pkgng/tasks/freebsd.yml | 551 +++ .../targets/pkgng/tasks/install_single_package.yml | 58 + .../tests/integration/targets/pkgng/tasks/main.yml | 8 + .../targets/pkgng/tasks/setup-testjail.yml | 100 + .../targets/pkgng/templates/MANIFEST.json.j2 | 16 + .../pkgng/templates/MANIFEST.json.j2.license | 3 + .../tests/integration/targets/pkgng/vars/main.yml | 9 + .../tests/integration/targets/pkgutil/aliases | 6 + .../integration/targets/pkgutil/tasks/main.yml | 117 + .../tests/integration/targets/proxmox/aliases | 9 + .../integration/targets/proxmox/tasks/main.yml | 579 +++ .../targets/python_requirements_info/aliases | 5 + .../python_requirements_info/tasks/main.yml | 45 + .../tests/integration/targets/read_csv/aliases | 5 + .../integration/targets/read_csv/meta/main.yml | 7 + .../integration/targets/read_csv/tasks/main.yml | 176 + .../tests/integration/targets/redis_info/aliases | 10 + .../targets/redis_info/defaults/main.yml | 8 + .../integration/targets/redis_info/meta/main.yml | 7 + .../integration/targets/redis_info/tasks/main.yml | 48 + .../tests/integration/targets/rundeck/aliases | 12 + .../integration/targets/rundeck/defaults/main.yml | 8 + .../targets/rundeck/files/test_job.yaml | 28 + .../integration/targets/rundeck/meta/main.yml | 7 + .../integration/targets/rundeck/tasks/main.yml | 127 + .../integration/targets/scaleway_compute/aliases | 6 + .../targets/scaleway_compute/defaults/main.yml | 13 + .../targets/scaleway_compute/tasks/ip.yml | 206 + .../targets/scaleway_compute/tasks/main.yml | 14 + .../targets/scaleway_compute/tasks/pagination.yml | 76 + .../scaleway_compute/tasks/security_group.yml | 152 + .../targets/scaleway_compute/tasks/state.yml | 392 ++ .../integration/targets/scaleway_container/aliases | 6 + .../targets/scaleway_container/defaults/main.yml | 18 + .../targets/scaleway_container/tasks/main.yml | 290 ++ .../targets/scaleway_container_info/aliases | 6 + .../scaleway_container_info/defaults/main.yml | 16 + .../targets/scaleway_container_info/tasks/main.yml | 63 + .../targets/scaleway_container_namespace/aliases | 6 + .../scaleway_container_namespace/defaults/main.yml | 15 + .../scaleway_container_namespace/tasks/main.yml | 255 ++ .../scaleway_container_namespace_info/aliases | 6 + .../defaults/main.yml | 13 + .../tasks/main.yml | 41 + .../targets/scaleway_container_registry/aliases | 6 + .../scaleway_container_registry/defaults/main.yml | 9 + .../scaleway_container_registry/tasks/main.yml | 178 + .../scaleway_container_registry_info/aliases | 6 + .../defaults/main.yml | 9 + .../tasks/main.yml | 41 + .../targets/scaleway_database_backup/aliases | 6 + .../scaleway_database_backup/defaults/main.yml | 9 + .../scaleway_database_backup/tasks/main.yml | 238 ++ .../integration/targets/scaleway_function/aliases | 6 + .../targets/scaleway_function/defaults/main.yml | 17 + .../targets/scaleway_function/tasks/main.yml | 284 ++ .../targets/scaleway_function_info/aliases | 6 + .../scaleway_function_info/defaults/main.yml | 15 + .../targets/scaleway_function_info/tasks/main.yml | 62 + .../targets/scaleway_function_namespace/aliases | 6 + .../scaleway_function_namespace/defaults/main.yml | 15 + .../scaleway_function_namespace/tasks/main.yml | 260 ++ .../scaleway_function_namespace_info/aliases | 6 + .../defaults/main.yml | 13 + .../tasks/main.yml | 43 + .../targets/scaleway_image_info/aliases | 6 + .../targets/scaleway_image_info/tasks/main.yml | 37 + .../tests/integration/targets/scaleway_ip/aliases | 6 + .../targets/scaleway_ip/defaults/main.yml | 11 + .../integration/targets/scaleway_ip/tasks/main.yml | 449 +++ .../integration/targets/scaleway_ip_info/aliases | 6 + .../targets/scaleway_ip_info/tasks/main.yml | 37 + .../tests/integration/targets/scaleway_lb/aliases | 6 + .../targets/scaleway_lb/defaults/main.yml | 12 + .../integration/targets/scaleway_lb/tasks/main.yml | 224 + .../targets/scaleway_organization_info/aliases | 6 + .../scaleway_organization_info/tasks/main.yml | 22 + .../targets/scaleway_security_group/aliases | 6 + .../scaleway_security_group/defaults/main.yml | 7 + .../targets/scaleway_security_group/tasks/main.yml | 139 + .../targets/scaleway_security_group_info/aliases | 6 + .../scaleway_security_group_info/tasks/main.yml | 37 + .../targets/scaleway_security_group_rule/aliases | 6 + .../scaleway_security_group_rule/defaults/main.yml | 12 + .../scaleway_security_group_rule/tasks/main.yml | 252 ++ .../targets/scaleway_server_info/aliases | 6 + .../targets/scaleway_server_info/tasks/main.yml | 37 + .../targets/scaleway_snapshot_info/aliases | 6 + .../targets/scaleway_snapshot_info/tasks/main.yml | 37 + .../integration/targets/scaleway_sshkey/aliases | 6 + .../targets/scaleway_sshkey/tasks/main.yml | 49 + .../integration/targets/scaleway_user_data/aliases | 6 + .../targets/scaleway_user_data/defaults/main.yml | 18 + .../targets/scaleway_user_data/tasks/main.yml | 87 + .../integration/targets/scaleway_volume/aliases | 6 + .../targets/scaleway_volume/defaults/main.yml | 7 + .../targets/scaleway_volume/tasks/main.yml | 51 + .../targets/scaleway_volume_info/aliases | 6 + .../targets/scaleway_volume_info/tasks/main.yml | 37 + .../tests/integration/targets/sefcontext/aliases | 7 + .../integration/targets/sefcontext/meta/main.yml | 7 + .../integration/targets/sefcontext/tasks/main.yml | 21 + .../targets/sefcontext/tasks/sefcontext.yml | 233 ++ .../tests/integration/targets/sensu_client/aliases | 6 + .../targets/sensu_client/tasks/main.yml | 179 + .../integration/targets/sensu_handler/aliases | 6 + .../targets/sensu_handler/tasks/main.yml | 129 + .../targets/sensu_handler/tasks/pipe.yml | 25 + .../targets/sensu_handler/tasks/set.yml | 53 + .../targets/sensu_handler/tasks/tcp.yml | 56 + .../targets/sensu_handler/tasks/transport.yml | 56 + .../targets/sensu_handler/tasks/udp.yml | 56 + .../targets/setup_cron/defaults/main.yml | 6 + .../integration/targets/setup_cron/meta/main.yml | 7 + .../integration/targets/setup_cron/tasks/main.yml | 75 + .../integration/targets/setup_cron/vars/alpine.yml | 8 + .../targets/setup_cron/vars/archlinux.yml | 8 + .../integration/targets/setup_cron/vars/debian.yml | 8 + .../targets/setup_cron/vars/default.yml | 4 + .../integration/targets/setup_cron/vars/fedora.yml | 8 + .../targets/setup_cron/vars/freebsd.yml | 8 + .../integration/targets/setup_cron/vars/redhat.yml | 9 + .../integration/targets/setup_cron/vars/suse.yml | 8 + .../integration/targets/setup_docker/README.md | 73 + .../tests/integration/targets/setup_docker/aliases | 5 + .../targets/setup_docker/defaults/main.yml | 14 + .../targets/setup_docker/handlers/main.yml | 19 + .../integration/targets/setup_docker/meta/main.yml | 7 + .../targets/setup_docker/tasks/D-Fedora.yml | 33 + .../targets/setup_docker/tasks/default.yml | 21 + .../targets/setup_docker/tasks/main.yml | 55 + .../targets/setup_docker/vars/D-Fedora.yml | 10 + .../targets/setup_docker/vars/D-Ubuntu.yml | 7 + .../integration/targets/setup_epel/tasks/main.yml | 25 + .../targets/setup_etcd3/defaults/main.yml | 17 + .../integration/targets/setup_etcd3/meta/main.yml | 7 + .../integration/targets/setup_etcd3/tasks/main.yml | 104 + .../targets/setup_etcd3/vars/RedHat-7.yml | 6 + .../targets/setup_etcd3/vars/Suse-py3.yml | 8 + .../integration/targets/setup_etcd3/vars/Suse.yml | 8 + .../targets/setup_etcd3/vars/default.yml | 6 + .../targets/setup_flatpak_remote/README.md | 144 + .../targets/setup_flatpak_remote/create-repo.sh | 63 + .../targets/setup_flatpak_remote/files/repo.tar.xz | Bin 0 -> 7352 bytes .../setup_flatpak_remote/files/repo.tar.xz.license | 3 + .../setup_flatpak_remote/handlers/main.yaml | 9 + .../targets/setup_flatpak_remote/meta/main.yaml | 7 + .../targets/setup_flatpak_remote/tasks/main.yaml | 32 + .../targets/setup_gnutar/handlers/main.yml | 11 + .../targets/setup_gnutar/tasks/main.yml | 24 + .../targets/setup_influxdb/tasks/main.yml | 12 + .../targets/setup_influxdb/tasks/setup.yml | 29 + .../targets/setup_java_keytool/meta/main.yml | 8 + .../targets/setup_java_keytool/tasks/main.yml | 26 + .../targets/setup_java_keytool/vars/Alpine.yml | 6 + .../targets/setup_java_keytool/vars/Archlinux.yml | 6 + .../targets/setup_java_keytool/vars/Debian.yml | 6 + .../targets/setup_java_keytool/vars/RedHat.yml | 6 + .../targets/setup_java_keytool/vars/Suse.yml | 6 + .../targets/setup_mosquitto/files/mosquitto.conf | 39 + .../targets/setup_mosquitto/meta/main.yml | 7 + .../targets/setup_mosquitto/tasks/main.yml | 12 + .../targets/setup_mosquitto/tasks/ubuntu.yml | 29 + .../setup_openldap/files/initial_config.ldif | 22 + .../files/initial_config.ldif.license | 3 + .../setup_openldap/files/rootpw_cnconfig.ldif | 4 + .../files/rootpw_cnconfig.ldif.license | 3 + .../targets/setup_openldap/meta/main.yml | 7 + .../targets/setup_openldap/tasks/main.yml | 72 + .../targets/setup_openldap/vars/Debian.yml | 60 + .../targets/setup_openldap/vars/Ubuntu.yml | 60 + .../targets/setup_opennebula/meta/main.yml | 7 + .../targets/setup_opennebula/tasks/main.yml | 9 + .../targets/setup_opennebula/vars/main.yml | 9 + .../targets/setup_openssl/meta/main.yml | 8 + .../targets/setup_openssl/tasks/main.yml | 69 + .../targets/setup_openssl/vars/Alpine.yml | 11 + .../targets/setup_openssl/vars/Archlinux.yml | 11 + .../targets/setup_openssl/vars/CentOS-8.yml | 9 + .../targets/setup_openssl/vars/Darwin.yml | 6 + .../targets/setup_openssl/vars/Debian.yml | 11 + .../targets/setup_openssl/vars/FreeBSD.yml | 11 + .../targets/setup_openssl/vars/RedHat-9.yml | 9 + .../targets/setup_openssl/vars/RedHat.yml | 11 + .../targets/setup_openssl/vars/Suse.yml | 11 + .../targets/setup_pkg_mgr/tasks/archlinux.yml | 23 + .../targets/setup_pkg_mgr/tasks/main.yml | 39 + .../targets/setup_postgresql_db/defaults/main.yml | 22 + .../setup_postgresql_db/files/dummy--1.0.sql | 6 + .../setup_postgresql_db/files/dummy--2.0.sql | 6 + .../setup_postgresql_db/files/dummy--3.0.sql | 6 + .../setup_postgresql_db/files/dummy.control | 3 + .../files/dummy.control.license | 3 + .../targets/setup_postgresql_db/files/pg_hba.conf | 14 + .../targets/setup_postgresql_db/meta/main.yml | 7 + .../targets/setup_postgresql_db/tasks/main.yml | 257 ++ .../setup_postgresql_db/vars/Alpine-py3.yml | 11 + .../setup_postgresql_db/vars/Archlinux-py3.yml | 11 + .../setup_postgresql_db/vars/Debian-11-py3.yml | 13 + .../targets/setup_postgresql_db/vars/Debian-8.yml | 13 + .../setup_postgresql_db/vars/FreeBSD-11-py3.yml | 17 + .../setup_postgresql_db/vars/FreeBSD-11.yml | 17 + .../setup_postgresql_db/vars/FreeBSD-12.0-py3.yml | 17 + .../setup_postgresql_db/vars/FreeBSD-12.0.yml | 17 + .../setup_postgresql_db/vars/FreeBSD-12.1-py3.yml | 17 + .../setup_postgresql_db/vars/FreeBSD-12.1.yml | 17 + .../setup_postgresql_db/vars/RedHat-py3.yml | 13 + .../targets/setup_postgresql_db/vars/RedHat.yml | 12 + .../targets/setup_postgresql_db/vars/Ubuntu-12.yml | 13 + .../targets/setup_postgresql_db/vars/Ubuntu-14.yml | 13 + .../setup_postgresql_db/vars/Ubuntu-16-py3.yml | 13 + .../targets/setup_postgresql_db/vars/Ubuntu-16.yml | 13 + .../setup_postgresql_db/vars/Ubuntu-18-py3.yml | 13 + .../setup_postgresql_db/vars/Ubuntu-20-py3.yml | 13 + .../setup_postgresql_db/vars/Ubuntu-22-py3.yml | 13 + .../setup_postgresql_db/vars/default-py3.yml | 11 + .../targets/setup_postgresql_db/vars/default.yml | 11 + .../setup_redis_replication/defaults/main.yml | 56 + .../setup_redis_replication/handlers/main.yml | 39 + .../targets/setup_redis_replication/meta/main.yml | 8 + .../targets/setup_redis_replication/tasks/main.yml | 12 + .../tasks/setup_redis_cluster.yml | 78 + .../targets/setup_remote_constraints/aliases | 5 + .../targets/setup_remote_constraints/meta/main.yml | 7 + .../setup_remote_constraints/tasks/main.yml | 18 + .../targets/setup_remote_tmp_dir/handlers/main.yml | 10 + .../setup_remote_tmp_dir/tasks/default-cleanup.yml | 10 + .../targets/setup_remote_tmp_dir/tasks/default.yml | 16 + .../targets/setup_remote_tmp_dir/tasks/main.yml | 20 + .../handlers/main.yml | 10 + .../tasks/default-cleanup.yml | 10 + .../tasks/default.yml | 22 + .../tasks/main.yml | 20 + .../targets/setup_rundeck/defaults/main.yml | 7 + .../targets/setup_rundeck/meta/main.yml | 7 + .../targets/setup_rundeck/tasks/main.yml | 41 + .../targets/setup_rundeck/vars/Alpine.yml | 6 + .../targets/setup_rundeck/vars/Archlinux.yml | 6 + .../targets/setup_rundeck/vars/Debian.yml | 6 + .../targets/setup_rundeck/vars/RedHat.yml | 6 + .../tests/integration/targets/setup_snap/aliases | 5 + .../targets/setup_snap/defaults/main.yml | 9 + .../targets/setup_snap/handlers/main.yml | 9 + .../integration/targets/setup_snap/meta/main.yml | 7 + .../targets/setup_snap/tasks/D-Fedora.yml | 25 + .../targets/setup_snap/tasks/D-RedHat-8.2.yml | 6 + .../targets/setup_snap/tasks/D-RedHat-8.3.yml | 6 + .../targets/setup_snap/tasks/D-RedHat-9.0.yml | 6 + .../targets/setup_snap/tasks/D-RedHat-9.1.yml | 6 + .../targets/setup_snap/tasks/D-Ubuntu.yml | 19 + .../targets/setup_snap/tasks/Debian.yml | 25 + .../targets/setup_snap/tasks/RedHat.yml | 25 + .../targets/setup_snap/tasks/default.yml | 25 + .../integration/targets/setup_snap/tasks/main.yml | 34 + .../targets/setup_snap/tasks/nothing.yml | 6 + .../targets/setup_tls/files/ca_certificate.pem | 23 + .../integration/targets/setup_tls/files/ca_key.pem | 32 + .../targets/setup_tls/files/client_certificate.pem | 24 + .../targets/setup_tls/files/client_key.pem | 31 + .../targets/setup_tls/files/server_certificate.pem | 24 + .../targets/setup_tls/files/server_key.pem | 31 + .../integration/targets/setup_tls/tasks/main.yml | 30 + .../targets/setup_wildfly_server/defaults/main.yml | 13 + .../setup_wildfly_server/files/wildfly.conf | 12 + .../targets/setup_wildfly_server/handlers/main.yml | 18 + .../targets/setup_wildfly_server/meta/main.yml | 8 + .../targets/setup_wildfly_server/tasks/main.yml | 107 + .../setup_wildfly_server/templates/launch.sh.j2 | 14 + .../templates/wildfly.service.j2 | 20 + .../tests/integration/targets/shutdown/aliases | 5 + .../integration/targets/shutdown/tasks/main.yml | 93 + .../general/tests/integration/targets/snap/aliases | 13 + .../tests/integration/targets/snap/meta/main.yml | 7 + .../tests/integration/targets/snap/tasks/main.yml | 243 ++ .../tests/integration/targets/snap_alias/aliases | 13 + .../integration/targets/snap_alias/meta/main.yml | 7 + .../integration/targets/snap_alias/tasks/main.yml | 13 + .../integration/targets/snap_alias/tasks/test.yml | 159 + .../targets/spectrum_model_attrs/aliases | 5 + .../targets/spectrum_model_attrs/tasks/main.yml | 78 + .../tests/integration/targets/ssh_config/aliases | 9 + .../targets/ssh_config/files/fake_id_rsa | 0 .../targets/ssh_config/files/ssh_config_test | 0 .../integration/targets/ssh_config/meta/main.yml | 8 + .../integration/targets/ssh_config/tasks/main.yml | 245 ++ .../targets/ssh_config/tasks/options.yml | 422 ++ .../tests/integration/targets/sudoers/aliases | 5 + .../integration/targets/sudoers/tasks/main.yml | 279 ++ .../integration/targets/supervisorctl/aliases | 8 + .../supervisorctl/files/sendProcessStdin.py | 31 + .../targets/supervisorctl/meta/main.yml | 8 + .../targets/supervisorctl/tasks/install_Darwin.yml | 9 + .../supervisorctl/tasks/install_FreeBSD.yml | 9 + .../targets/supervisorctl/tasks/install_Linux.yml | 15 + .../targets/supervisorctl/tasks/install_RedHat.yml | 9 + .../targets/supervisorctl/tasks/install_Suse.yml | 9 + .../targets/supervisorctl/tasks/install_pip.yml | 9 + .../targets/supervisorctl/tasks/main.yml | 57 + .../supervisorctl/tasks/start_supervisord.yml | 14 + .../supervisorctl/tasks/stop_supervisord.yml | 7 + .../targets/supervisorctl/tasks/test.yml | 17 + .../targets/supervisorctl/tasks/test_start.yml | 140 + .../targets/supervisorctl/tasks/test_stop.yml | 64 + .../supervisorctl/tasks/uninstall_Darwin.yml | 9 + .../supervisorctl/tasks/uninstall_FreeBSD.yml | 9 + .../supervisorctl/tasks/uninstall_Linux.yml | 9 + .../supervisorctl/tasks/uninstall_RedHat.yml | 9 + .../targets/supervisorctl/tasks/uninstall_Suse.yml | 9 + .../targets/supervisorctl/tasks/uninstall_pip.yml | 9 + .../supervisorctl/templates/supervisord.conf | 48 + .../targets/supervisorctl/vars/Debian.yml | 6 + .../targets/supervisorctl/vars/defaults.yml | 6 + .../tests/integration/targets/sysrc/aliases | 9 + .../tests/integration/targets/sysrc/tasks/main.yml | 343 ++ .../targets/sysrc/tasks/setup-testjail.yml | 73 + .../tests/integration/targets/terraform/.gitignore | 8 + .../tests/integration/targets/terraform/aliases | 11 + .../terraform/files/complex_variables/main.tf | 35 + .../terraform/files/complex_variables/variables.tf | 62 + .../integration/targets/terraform/meta/main.yml | 8 + .../targets/terraform/tasks/complex_variables.yml | 60 + .../integration/targets/terraform/tasks/main.yml | 67 + .../terraform/tasks/test_provider_upgrade.yml | 35 + .../terraform/templates/provider_test/main.tf.j2 | 13 + .../integration/targets/terraform/vars/main.yml | 40 + .../integration/targets/test_a_module/aliases | 5 + .../ansible_collections/testns/testcoll/galaxy.yml | 12 + .../testcoll/plugins/modules/collection_module.py | 33 + .../targets/test_a_module/library/local_module.py | 33 + .../integration/targets/test_a_module/runme.sh | 20 + .../integration/targets/test_a_module/runme.yml | 42 + .../tests/integration/targets/timezone/aliases | 9 + .../integration/targets/timezone/meta/main.yml | 7 + .../integration/targets/timezone/tasks/main.yml | 96 + .../integration/targets/timezone/tasks/test.yml | 612 +++ .../general/tests/integration/targets/ufw/aliases | 17 + .../tests/integration/targets/ufw/meta/main.yml | 7 + .../tests/integration/targets/ufw/tasks/main.yml | 45 + .../integration/targets/ufw/tasks/run-test.yml | 25 + .../integration/targets/ufw/tasks/tests/basic.yml | 406 ++ .../targets/ufw/tasks/tests/global-state.yml | 154 + .../targets/ufw/tasks/tests/insert_relative_to.yml | 84 + .../targets/ufw/tasks/tests/interface.yml | 86 + .../tests/integration/targets/wakeonlan/aliases | 6 + .../integration/targets/wakeonlan/tasks/main.yml | 58 + .../tests/integration/targets/xattr/aliases | 12 + .../integration/targets/xattr/defaults/main.yml | 6 + .../tests/integration/targets/xattr/meta/main.yml | 8 + .../tests/integration/targets/xattr/tasks/main.yml | 21 + .../integration/targets/xattr/tasks/setup.yml | 14 + .../tests/integration/targets/xattr/tasks/test.yml | 72 + .../tests/integration/targets/xfs_quota/aliases | 12 + .../targets/xfs_quota/defaults/main.yml | 46 + .../integration/targets/xfs_quota/meta/main.yml | 7 + .../integration/targets/xfs_quota/tasks/gquota.yml | 147 + .../integration/targets/xfs_quota/tasks/main.yml | 37 + .../integration/targets/xfs_quota/tasks/pquota.yml | 184 + .../integration/targets/xfs_quota/tasks/uquota.yml | 147 + .../general/tests/integration/targets/xml/aliases | 7 + .../xml/fixtures/ansible-xml-beers-unicode.xml | 13 + .../fixtures/ansible-xml-beers-unicode.xml.license | 3 + .../targets/xml/fixtures/ansible-xml-beers.xml | 14 + .../xml/fixtures/ansible-xml-beers.xml.license | 3 + .../xml/fixtures/ansible-xml-namespaced-beers.xml | 14 + .../ansible-xml-namespaced-beers.xml.license | 3 + .../tests/integration/targets/xml/meta/main.yml | 7 + .../results/test-add-children-elements-unicode.xml | 14 + .../test-add-children-elements-unicode.xml.license | 3 + .../xml/results/test-add-children-elements.xml | 14 + .../results/test-add-children-elements.xml.license | 3 + .../results/test-add-children-from-groupvars.xml | 14 + .../test-add-children-from-groupvars.xml.license | 3 + .../xml/results/test-add-children-insertafter.xml | 17 + .../test-add-children-insertafter.xml.license | 3 + .../xml/results/test-add-children-insertbefore.xml | 17 + .../test-add-children-insertbefore.xml.license | 3 + .../test-add-children-with-attributes-unicode.xml | 14 + ...dd-children-with-attributes-unicode.xml.license | 3 + .../results/test-add-children-with-attributes.xml | 14 + .../test-add-children-with-attributes.xml.license | 3 + .../xml/results/test-add-element-implicitly.xml | 32 + .../test-add-element-implicitly.xml.license | 3 + .../test-add-namespaced-children-elements.xml | 14 + ...st-add-namespaced-children-elements.xml.license | 3 + .../targets/xml/results/test-pretty-print-only.xml | 14 + .../xml/results/test-pretty-print-only.xml.license | 3 + .../targets/xml/results/test-pretty-print.xml | 15 + .../xml/results/test-pretty-print.xml.license | 3 + .../targets/xml/results/test-remove-attribute.xml | 14 + .../xml/results/test-remove-attribute.xml.license | 3 + .../targets/xml/results/test-remove-element.xml | 13 + .../xml/results/test-remove-element.xml.license | 3 + .../results/test-remove-namespaced-attribute.xml | 14 + .../test-remove-namespaced-attribute.xml.license | 3 + .../xml/results/test-remove-namespaced-element.xml | 13 + .../test-remove-namespaced-element.xml.license | 3 + .../results/test-set-attribute-value-unicode.xml | 14 + .../test-set-attribute-value-unicode.xml.license | 3 + .../xml/results/test-set-attribute-value.xml | 14 + .../results/test-set-attribute-value.xml.license | 3 + .../test-set-children-elements-empty-list.xml | 11 + ...st-set-children-elements-empty-list.xml.license | 3 + .../results/test-set-children-elements-level.xml | 11 + .../test-set-children-elements-level.xml.license | 3 + .../results/test-set-children-elements-unicode.xml | 11 + .../test-set-children-elements-unicode.xml.license | 3 + .../xml/results/test-set-children-elements.xml | 11 + .../results/test-set-children-elements.xml.license | 3 + .../xml/results/test-set-element-value-empty.xml | 14 + .../test-set-element-value-empty.xml.license | 3 + .../xml/results/test-set-element-value-unicode.xml | 14 + .../test-set-element-value-unicode.xml.license | 3 + .../targets/xml/results/test-set-element-value.xml | 14 + .../xml/results/test-set-element-value.xml.license | 3 + .../test-set-namespaced-attribute-value.xml | 14 + ...test-set-namespaced-attribute-value.xml.license | 3 + .../results/test-set-namespaced-element-value.xml | 14 + .../test-set-namespaced-element-value.xml.license | 3 + .../tests/integration/targets/xml/tasks/main.yml | 77 + .../tasks/test-add-children-elements-unicode.yml | 36 + .../xml/tasks/test-add-children-elements.yml | 36 + .../xml/tasks/test-add-children-from-groupvars.yml | 35 + .../xml/tasks/test-add-children-insertafter.yml | 36 + .../xml/tasks/test-add-children-insertbefore.yml | 36 + .../test-add-children-with-attributes-unicode.yml | 38 + .../tasks/test-add-children-with-attributes.yml | 42 + .../xml/tasks/test-add-element-implicitly.yml | 241 ++ .../test-add-namespaced-children-elements.yml | 39 + .../xml/tasks/test-children-elements-xml.yml | 37 + .../targets/xml/tasks/test-count-unicode.yml | 23 + .../integration/targets/xml/tasks/test-count.yml | 23 + .../xml/tasks/test-get-element-content-unicode.yml | 36 + .../targets/xml/tasks/test-get-element-content.yml | 51 + .../tasks/test-mutually-exclusive-attributes.yml | 26 + .../targets/xml/tasks/test-pretty-print-only.yml | 33 + .../targets/xml/tasks/test-pretty-print.yml | 34 + .../xml/tasks/test-remove-attribute-nochange.yml | 32 + .../targets/xml/tasks/test-remove-attribute.yml | 35 + .../xml/tasks/test-remove-element-nochange.yml | 32 + .../targets/xml/tasks/test-remove-element.yml | 35 + .../test-remove-namespaced-attribute-nochange.yml | 37 + .../xml/tasks/test-remove-namespaced-attribute.yml | 40 + .../test-remove-namespaced-element-nochange.yml | 37 + .../xml/tasks/test-remove-namespaced-element.yml | 40 + .../xml/tasks/test-set-attribute-value-unicode.yml | 36 + .../targets/xml/tasks/test-set-attribute-value.yml | 36 + .../xml/tasks/test-set-children-elements-level.yml | 81 + .../tasks/test-set-children-elements-unicode.yml | 53 + .../xml/tasks/test-set-children-elements.yml | 86 + .../xml/tasks/test-set-element-value-empty.yml | 35 + .../xml/tasks/test-set-element-value-unicode.yml | 50 + .../targets/xml/tasks/test-set-element-value.yml | 50 + .../tasks/test-set-namespaced-attribute-value.yml | 41 + .../test-set-namespaced-children-elements.yml | 61 + .../tasks/test-set-namespaced-element-value.yml | 53 + .../targets/xml/tasks/test-xmlstring.yml | 85 + .../tests/integration/targets/xml/vars/main.yml | 11 + .../general/tests/integration/targets/yarn/aliases | 8 + .../tests/integration/targets/yarn/meta/main.yml | 9 + .../tests/integration/targets/yarn/tasks/main.yml | 23 + .../tests/integration/targets/yarn/tasks/run.yml | 233 ++ .../integration/targets/yarn/templates/package.j2 | 14 + .../integration/targets/yum_versionlock/aliases | 11 + .../targets/yum_versionlock/tasks/main.yml | 87 + .../tests/integration/targets/zypper/aliases | 11 + .../integration/targets/zypper/files/empty.spec | 12 + .../targets/zypper/files/empty.spec.license | 3 + .../tests/integration/targets/zypper/meta/main.yml | 7 + .../integration/targets/zypper/tasks/main.yml | 15 + .../integration/targets/zypper/tasks/zypper.yml | 530 +++ .../targets/zypper/templates/duplicate.spec.j2 | 24 + .../integration/targets/zypper_repository/aliases | 11 + .../files/systemsmanagement_Uyuni_Utils.repo | 11 + .../targets/zypper_repository/meta/main.yml | 7 + .../targets/zypper_repository/tasks/main.yml | 13 + .../targets/zypper_repository/tasks/test.yml | 40 + .../zypper_repository/tasks/zypper_repository.yml | 289 ++ .../general/tests/sanity/extra/aliases.json | 11 + .../tests/sanity/extra/aliases.json.license | 3 + .../general/tests/sanity/extra/aliases.py | 65 + .../general/tests/sanity/extra/botmeta.json | 8 + .../tests/sanity/extra/botmeta.json.license | 3 + .../general/tests/sanity/extra/botmeta.py | 234 ++ .../general/tests/sanity/extra/extra-docs.json | 13 + .../tests/sanity/extra/extra-docs.json.license | 3 + .../general/tests/sanity/extra/extra-docs.py | 29 + .../general/tests/sanity/extra/licenses.json | 4 + .../tests/sanity/extra/licenses.json.license | 3 + .../general/tests/sanity/extra/licenses.py | 110 + .../general/tests/sanity/extra/licenses.py.license | 3 + .../tests/sanity/extra/no-unwanted-files.json | 7 + .../sanity/extra/no-unwanted-files.json.license | 3 + .../tests/sanity/extra/no-unwanted-files.py | 58 + .../community/general/tests/sanity/ignore-2.11.txt | 28 + .../general/tests/sanity/ignore-2.11.txt.license | 3 + .../community/general/tests/sanity/ignore-2.12.txt | 21 + .../general/tests/sanity/ignore-2.12.txt.license | 3 + .../community/general/tests/sanity/ignore-2.13.txt | 21 + .../general/tests/sanity/ignore-2.13.txt.license | 3 + .../community/general/tests/sanity/ignore-2.14.txt | 23 + .../general/tests/sanity/ignore-2.14.txt.license | 3 + .../community/general/tests/sanity/ignore-2.15.txt | 23 + .../general/tests/sanity/ignore-2.15.txt.license | 3 + .../community/general/tests/sanity/ignore-2.16.txt | 23 + .../general/tests/sanity/ignore-2.16.txt.license | 3 + .../general/tests/unit/compat/__init__.py | 0 .../general/tests/unit/compat/builtins.py | 20 + .../community/general/tests/unit/compat/mock.py | 109 + .../general/tests/unit/compat/unittest.py | 25 + .../community/general/tests/unit/mock/loader.py | 103 + .../community/general/tests/unit/mock/path.py | 12 + .../community/general/tests/unit/mock/procenv.py | 77 + .../general/tests/unit/mock/vault_helper.py | 29 + .../general/tests/unit/mock/yaml_helper.py | 128 + .../general/tests/unit/plugins/become/conftest.py | 38 + .../general/tests/unit/plugins/become/helper.py | 19 + .../general/tests/unit/plugins/become/test_doas.py | 85 + .../general/tests/unit/plugins/become/test_dzdo.py | 95 + .../general/tests/unit/plugins/become/test_ksu.py | 86 + .../tests/unit/plugins/become/test_pbrun.py | 85 + .../tests/unit/plugins/become/test_pfexec.py | 82 + .../tests/unit/plugins/become/test_sudosu.py | 51 + .../tests/unit/plugins/cache/test_memcached.py | 18 + .../general/tests/unit/plugins/cache/test_redis.py | 26 + .../tests/unit/plugins/callback/test_elastic.py | 127 + .../unit/plugins/callback/test_loganalytics.py | 66 + .../unit/plugins/callback/test_opentelemetry.py | 212 + .../tests/unit/plugins/callback/test_splunk.py | 64 + .../tests/unit/plugins/connection/test_lxc.py | 25 + .../tests/unit/plugins/filter/test_crc32.py | 16 + .../plugins/inventory/fixtures/lxd_inventory.atd | 174 + .../inventory/fixtures/lxd_inventory.atd.license | 3 + .../inventory/fixtures/opennebula_inventory.json | 222 + .../fixtures/opennebula_inventory.json.license | 3 + .../tests/unit/plugins/inventory/test_cobbler.py | 31 + .../tests/unit/plugins/inventory/test_icinga2.py | 149 + .../tests/unit/plugins/inventory/test_linode.py | 47 + .../tests/unit/plugins/inventory/test_lxd.py | 106 + .../unit/plugins/inventory/test_opennebula.py | 342 ++ .../tests/unit/plugins/inventory/test_proxmox.py | 745 ++++ .../plugins/inventory/test_stackpath_compute.py | 206 + .../unit/plugins/inventory/test_xen_orchestra.py | 211 + .../unit/plugins/lookup/onepassword_common.py | 85 + .../unit/plugins/lookup/onepassword_conftest.py | 39 + .../lookup/onepassword_fixtures/v1_out_01.json | 18 + .../onepassword_fixtures/v1_out_01.json.license | 3 + .../lookup/onepassword_fixtures/v1_out_02.json | 18 + .../onepassword_fixtures/v1_out_02.json.license | 3 + .../lookup/onepassword_fixtures/v1_out_03.json | 20 + .../onepassword_fixtures/v1_out_03.json.license | 3 + .../lookup/onepassword_fixtures/v2_out_01.json | 35 + .../onepassword_fixtures/v2_out_01.json.license | 3 + .../lookup/onepassword_fixtures/v2_out_02.json | 85 + .../onepassword_fixtures/v2_out_02.json.license | 3 + .../lookup/onepassword_fixtures/v2_out_03.json | 103 + .../onepassword_fixtures/v2_out_03.json.license | 3 + .../tests/unit/plugins/lookup/test_bitwarden.py | 160 + .../tests/unit/plugins/lookup/test_dependent.py | 45 + .../general/tests/unit/plugins/lookup/test_dsv.py | 44 + .../tests/unit/plugins/lookup/test_etcd3.py | 56 + .../tests/unit/plugins/lookup/test_lastpass.py | 175 + .../tests/unit/plugins/lookup/test_manifold.py | 537 +++ .../unit/plugins/lookup/test_merge_variables.py | 135 + .../tests/unit/plugins/lookup/test_onepassword.py | 268 ++ .../tests/unit/plugins/lookup/test_revbitspss.py | 44 + .../general/tests/unit/plugins/lookup/test_tss.py | 120 + .../plugins/module_utils/cloud/test_backoff.py | 54 + .../plugins/module_utils/cloud/test_scaleway.py | 126 + .../tests/unit/plugins/module_utils/conftest.py | 73 + .../module_utils/hwc/test_dict_comparison.py | 167 + .../plugins/module_utils/hwc/test_hwc_utils.py | 40 + .../identity/keycloak/test_keycloak_connect.py | 165 + .../keycloak/test_keycloak_module_utils.py | 103 + .../module_utils/net_tools/pritunl/test_api.py | 632 +++ .../unit/plugins/module_utils/test_cmd_runner.py | 374 ++ .../tests/unit/plugins/module_utils/test_csv.py | 166 + .../unit/plugins/module_utils/test_database.py | 143 + .../unit/plugins/module_utils/test_known_hosts.py | 117 + .../plugins/module_utils/test_module_helper.py | 239 ++ .../unit/plugins/module_utils/test_ocapi_utils.py | 54 + .../unit/plugins/module_utils/test_onepassword.py | 44 + .../unit/plugins/module_utils/test_opennebula.py | 102 + .../unit/plugins/module_utils/test_saslprep.py | 57 + .../unit/plugins/module_utils/test_utm_utils.py | 48 + .../module_utils/xenserver/FakeAnsibleModule.py | 34 + .../plugins/module_utils/xenserver/FakeXenAPI.py | 70 + .../unit/plugins/module_utils/xenserver/common.py | 26 + .../plugins/module_utils/xenserver/conftest.py | 119 + .../fixtures/ansible-test-vm-1-facts.json | 73 + .../fixtures/ansible-test-vm-1-facts.json.license | 3 + .../fixtures/ansible-test-vm-1-params.json | 707 ++++ .../fixtures/ansible-test-vm-1-params.json.license | 3 + .../fixtures/ansible-test-vm-2-facts.json | 87 + .../fixtures/ansible-test-vm-2-facts.json.license | 3 + .../fixtures/ansible-test-vm-2-params.json | 771 ++++ .../fixtures/ansible-test-vm-2-params.json.license | 3 + .../fixtures/ansible-test-vm-3-facts.json | 75 + .../fixtures/ansible-test-vm-3-facts.json.license | 3 + .../fixtures/ansible-test-vm-3-params.json | 420 ++ .../fixtures/ansible-test-vm-3-params.json.license | 3 + .../xenserver/test_gather_vm_params_and_facts.py | 75 + .../module_utils/xenserver/test_get_object_ref.py | 74 + .../plugins/module_utils/xenserver/test_misc.py | 18 + .../xenserver/test_netaddr_functions.py | 183 + .../xenserver/test_set_vm_power_state.py | 414 ++ .../xenserver/test_wait_for_functions.py | 221 + .../plugins/module_utils/xenserver/test_xapi.py | 176 + .../module_utils/xenserver/test_xenserverobject.py | 51 + .../unit/plugins/modules/FakeAnsibleModule.py | 34 + .../tests/unit/plugins/modules/FakeXenAPI.py | 70 + .../general/tests/unit/plugins/modules/conftest.py | 39 + .../general/tests/unit/plugins/modules/gitlab.py | 704 ++++ .../tests/unit/plugins/modules/hpe_test_utils.py | 206 + .../interfaces_file/interfaces_file-README.md | 27 + .../golden_output/address_family.test_no_changes | 12 + .../address_family.test_no_changes.json | 21 + .../address_family.test_no_changes.json.license | 3 + .../address_family.test_no_changes.license | 3 + .../golden_output/address_family_add_aggi_up | 12 + .../address_family_add_aggi_up.exceptions.txt | 8 + ...dress_family_add_aggi_up.exceptions.txt.license | 3 + .../golden_output/address_family_add_aggi_up.json | 21 + .../address_family_add_aggi_up.json.license | 3 + .../address_family_add_aggi_up.license | 3 + .../golden_output/address_family_add_aggi_up_twice | 12 + ...address_family_add_aggi_up_twice.exceptions.txt | 17 + ...family_add_aggi_up_twice.exceptions.txt.license | 3 + .../address_family_add_aggi_up_twice.json | 21 + .../address_family_add_aggi_up_twice.json.license | 3 + .../address_family_add_aggi_up_twice.license | 3 + .../address_family_add_and_delete_aggi_up | 12 + ...ss_family_add_and_delete_aggi_up.exceptions.txt | 17 + ...y_add_and_delete_aggi_up.exceptions.txt.license | 3 + .../address_family_add_and_delete_aggi_up.json | 21 + ...ress_family_add_and_delete_aggi_up.json.license | 3 + .../address_family_add_and_delete_aggi_up.license | 3 + .../golden_output/address_family_aggi_remove_dup | 12 + .../address_family_aggi_remove_dup.exceptions.txt | 17 + ...s_family_aggi_remove_dup.exceptions.txt.license | 3 + .../address_family_aggi_remove_dup.json | 21 + .../address_family_aggi_remove_dup.json.license | 3 + .../address_family_aggi_remove_dup.license | 3 + .../golden_output/address_family_change_ipv4 | 12 + .../address_family_change_ipv4.exceptions.txt | 0 .../golden_output/address_family_change_ipv4.json | 21 + .../address_family_change_ipv4.json.license | 3 + .../address_family_change_ipv4.license | 3 + .../address_family_change_ipv4_post_up | 13 + ...dress_family_change_ipv4_post_up.exceptions.txt | 0 .../address_family_change_ipv4_post_up.json | 21 + ...address_family_change_ipv4_post_up.json.license | 3 + .../address_family_change_ipv4_post_up.license | 3 + .../address_family_change_ipv4_pre_up | 13 + ...ddress_family_change_ipv4_pre_up.exceptions.txt | 0 .../address_family_change_ipv4_pre_up.json | 21 + .../address_family_change_ipv4_pre_up.json.license | 3 + .../address_family_change_ipv4_pre_up.license | 3 + .../golden_output/address_family_change_ipv6 | 12 + .../address_family_change_ipv6.exceptions.txt | 0 .../golden_output/address_family_change_ipv6.json | 21 + .../address_family_change_ipv6.json.license | 3 + .../address_family_change_ipv6.license | 3 + .../address_family_change_ipv6_post_up | 13 + ...dress_family_change_ipv6_post_up.exceptions.txt | 0 .../address_family_change_ipv6_post_up.json | 21 + ...address_family_change_ipv6_post_up.json.license | 3 + .../address_family_change_ipv6_post_up.license | 3 + .../address_family_change_ipv6_pre_up | 13 + ...ddress_family_change_ipv6_pre_up.exceptions.txt | 0 .../address_family_change_ipv6_pre_up.json | 21 + .../address_family_change_ipv6_pre_up.json.license | 3 + .../address_family_change_ipv6_pre_up.license | 3 + .../golden_output/address_family_change_method | 12 + .../address_family_change_method.exceptions.txt | 8 + ...ess_family_change_method.exceptions.txt.license | 3 + .../address_family_change_method.json | 21 + .../address_family_change_method.json.license | 3 + .../address_family_change_method.license | 3 + .../golden_output/address_family_revert | 12 + .../address_family_revert.exceptions.txt | 0 .../golden_output/address_family_revert.json | 21 + .../address_family_revert.json.license | 3 + .../golden_output/address_family_revert.license | 3 + .../address_family_set_aggi_and_eth0_mtu | 13 + ...ess_family_set_aggi_and_eth0_mtu.exceptions.txt | 8 + ...ly_set_aggi_and_eth0_mtu.exceptions.txt.license | 3 + .../address_family_set_aggi_and_eth0_mtu.json | 21 + ...dress_family_set_aggi_and_eth0_mtu.json.license | 3 + .../address_family_set_aggi_and_eth0_mtu.license | 3 + .../golden_output/address_family_set_aggi_slaves | 12 + .../address_family_set_aggi_slaves.exceptions.txt | 8 + ...s_family_set_aggi_slaves.exceptions.txt.license | 3 + .../address_family_set_aggi_slaves.json | 21 + .../address_family_set_aggi_slaves.json.license | 3 + .../address_family_set_aggi_slaves.license | 3 + .../golden_output/default_dhcp.test_no_changes | 6 + .../default_dhcp.test_no_changes.json | 18 + .../default_dhcp.test_no_changes.json.license | 3 + .../default_dhcp.test_no_changes.license | 3 + .../golden_output/default_dhcp_add_aggi_up | 6 + .../default_dhcp_add_aggi_up.exceptions.txt | 8 + ...default_dhcp_add_aggi_up.exceptions.txt.license | 3 + .../golden_output/default_dhcp_add_aggi_up.json | 18 + .../default_dhcp_add_aggi_up.json.license | 3 + .../golden_output/default_dhcp_add_aggi_up.license | 3 + .../golden_output/default_dhcp_add_aggi_up_twice | 6 + .../default_dhcp_add_aggi_up_twice.exceptions.txt | 17 + ...t_dhcp_add_aggi_up_twice.exceptions.txt.license | 3 + .../default_dhcp_add_aggi_up_twice.json | 18 + .../default_dhcp_add_aggi_up_twice.json.license | 3 + .../default_dhcp_add_aggi_up_twice.license | 3 + .../default_dhcp_add_and_delete_aggi_up | 6 + ...ault_dhcp_add_and_delete_aggi_up.exceptions.txt | 17 + ...p_add_and_delete_aggi_up.exceptions.txt.license | 3 + .../default_dhcp_add_and_delete_aggi_up.json | 18 + ...efault_dhcp_add_and_delete_aggi_up.json.license | 3 + .../default_dhcp_add_and_delete_aggi_up.license | 3 + .../golden_output/default_dhcp_aggi_remove_dup | 6 + .../default_dhcp_aggi_remove_dup.exceptions.txt | 17 + ...ult_dhcp_aggi_remove_dup.exceptions.txt.license | 3 + .../default_dhcp_aggi_remove_dup.json | 18 + .../default_dhcp_aggi_remove_dup.json.license | 3 + .../default_dhcp_aggi_remove_dup.license | 3 + .../golden_output/default_dhcp_change_ipv4 | 7 + .../default_dhcp_change_ipv4.exceptions.txt | 0 .../golden_output/default_dhcp_change_ipv4.json | 18 + .../default_dhcp_change_ipv4.json.license | 3 + .../golden_output/default_dhcp_change_ipv4.license | 3 + .../golden_output/default_dhcp_change_ipv4_post_up | 7 + ...default_dhcp_change_ipv4_post_up.exceptions.txt | 0 .../default_dhcp_change_ipv4_post_up.json | 18 + .../default_dhcp_change_ipv4_post_up.json.license | 3 + .../default_dhcp_change_ipv4_post_up.license | 3 + .../golden_output/default_dhcp_change_ipv4_pre_up | 7 + .../default_dhcp_change_ipv4_pre_up.exceptions.txt | 0 .../default_dhcp_change_ipv4_pre_up.json | 18 + .../default_dhcp_change_ipv4_pre_up.json.license | 3 + .../default_dhcp_change_ipv4_pre_up.license | 3 + .../golden_output/default_dhcp_change_ipv6 | 6 + .../default_dhcp_change_ipv6.exceptions.txt | 9 + ...default_dhcp_change_ipv6.exceptions.txt.license | 3 + .../golden_output/default_dhcp_change_ipv6.json | 18 + .../default_dhcp_change_ipv6.json.license | 3 + .../golden_output/default_dhcp_change_ipv6.license | 3 + .../golden_output/default_dhcp_change_ipv6_post_up | 6 + ...default_dhcp_change_ipv6_post_up.exceptions.txt | 9 + ...dhcp_change_ipv6_post_up.exceptions.txt.license | 3 + .../default_dhcp_change_ipv6_post_up.json | 18 + .../default_dhcp_change_ipv6_post_up.json.license | 3 + .../default_dhcp_change_ipv6_post_up.license | 3 + .../golden_output/default_dhcp_change_ipv6_pre_up | 6 + .../default_dhcp_change_ipv6_pre_up.exceptions.txt | 9 + ..._dhcp_change_ipv6_pre_up.exceptions.txt.license | 3 + .../default_dhcp_change_ipv6_pre_up.json | 18 + .../default_dhcp_change_ipv6_pre_up.json.license | 3 + .../default_dhcp_change_ipv6_pre_up.license | 3 + .../golden_output/default_dhcp_change_method | 6 + .../default_dhcp_change_method.exceptions.txt | 8 + ...fault_dhcp_change_method.exceptions.txt.license | 3 + .../golden_output/default_dhcp_change_method.json | 18 + .../default_dhcp_change_method.json.license | 3 + .../default_dhcp_change_method.license | 3 + .../golden_output/default_dhcp_revert | 6 + .../default_dhcp_revert.exceptions.txt | 0 .../golden_output/default_dhcp_revert.json | 18 + .../golden_output/default_dhcp_revert.json.license | 3 + .../golden_output/default_dhcp_revert.license | 3 + .../default_dhcp_set_aggi_and_eth0_mtu | 7 + ...fault_dhcp_set_aggi_and_eth0_mtu.exceptions.txt | 8 + ...cp_set_aggi_and_eth0_mtu.exceptions.txt.license | 3 + .../default_dhcp_set_aggi_and_eth0_mtu.json | 18 + ...default_dhcp_set_aggi_and_eth0_mtu.json.license | 3 + .../default_dhcp_set_aggi_and_eth0_mtu.license | 3 + .../golden_output/default_dhcp_set_aggi_slaves | 6 + .../default_dhcp_set_aggi_slaves.exceptions.txt | 8 + ...ult_dhcp_set_aggi_slaves.exceptions.txt.license | 3 + .../default_dhcp_set_aggi_slaves.json | 18 + .../default_dhcp_set_aggi_slaves.json.license | 3 + .../default_dhcp_set_aggi_slaves.license | 3 + .../no_leading_spaces.test_no_changes | 8 + .../no_leading_spaces.test_no_changes.json | 21 + .../no_leading_spaces.test_no_changes.json.license | 3 + .../no_leading_spaces.test_no_changes.license | 3 + .../golden_output/no_leading_spaces_add_aggi_up | 8 + .../no_leading_spaces_add_aggi_up.exceptions.txt | 8 + ...ading_spaces_add_aggi_up.exceptions.txt.license | 3 + .../no_leading_spaces_add_aggi_up.json | 21 + .../no_leading_spaces_add_aggi_up.json.license | 3 + .../no_leading_spaces_add_aggi_up.license | 3 + .../no_leading_spaces_add_aggi_up_twice | 8 + ...leading_spaces_add_aggi_up_twice.exceptions.txt | 17 + ...spaces_add_aggi_up_twice.exceptions.txt.license | 3 + .../no_leading_spaces_add_aggi_up_twice.json | 21 + ...o_leading_spaces_add_aggi_up_twice.json.license | 3 + .../no_leading_spaces_add_aggi_up_twice.license | 3 + .../no_leading_spaces_add_and_delete_aggi_up | 8 + ...ng_spaces_add_and_delete_aggi_up.exceptions.txt | 17 + ...s_add_and_delete_aggi_up.exceptions.txt.license | 3 + .../no_leading_spaces_add_and_delete_aggi_up.json | 21 + ...ding_spaces_add_and_delete_aggi_up.json.license | 3 + ...o_leading_spaces_add_and_delete_aggi_up.license | 3 + .../no_leading_spaces_aggi_remove_dup | 8 + ...o_leading_spaces_aggi_remove_dup.exceptions.txt | 17 + ...g_spaces_aggi_remove_dup.exceptions.txt.license | 3 + .../no_leading_spaces_aggi_remove_dup.json | 21 + .../no_leading_spaces_aggi_remove_dup.json.license | 3 + .../no_leading_spaces_aggi_remove_dup.license | 3 + .../golden_output/no_leading_spaces_change_ipv4 | 8 + .../no_leading_spaces_change_ipv4.exceptions.txt | 0 ...ading_spaces_change_ipv4.exceptions.txt.license | 3 + .../no_leading_spaces_change_ipv4.json | 21 + .../no_leading_spaces_change_ipv4.json.license | 3 + .../no_leading_spaces_change_ipv4.license | 3 + .../no_leading_spaces_change_ipv4_post_up | 9 + ...ading_spaces_change_ipv4_post_up.exceptions.txt | 0 ...aces_change_ipv4_post_up.exceptions.txt.license | 3 + .../no_leading_spaces_change_ipv4_post_up.json | 21 + ...leading_spaces_change_ipv4_post_up.json.license | 3 + .../no_leading_spaces_change_ipv4_post_up.license | 3 + .../no_leading_spaces_change_ipv4_pre_up | 9 + ...eading_spaces_change_ipv4_pre_up.exceptions.txt | 0 ...paces_change_ipv4_pre_up.exceptions.txt.license | 3 + .../no_leading_spaces_change_ipv4_pre_up.json | 21 + ..._leading_spaces_change_ipv4_pre_up.json.license | 3 + .../no_leading_spaces_change_ipv4_pre_up.license | 3 + .../golden_output/no_leading_spaces_change_ipv6 | 8 + .../no_leading_spaces_change_ipv6.exceptions.txt | 9 + ...ading_spaces_change_ipv6.exceptions.txt.license | 3 + .../no_leading_spaces_change_ipv6.json | 21 + .../no_leading_spaces_change_ipv6.json.license | 3 + .../no_leading_spaces_change_ipv6.license | 3 + .../no_leading_spaces_change_ipv6_post_up | 8 + ...ading_spaces_change_ipv6_post_up.exceptions.txt | 9 + ...aces_change_ipv6_post_up.exceptions.txt.license | 3 + .../no_leading_spaces_change_ipv6_post_up.json | 21 + ...leading_spaces_change_ipv6_post_up.json.license | 3 + .../no_leading_spaces_change_ipv6_post_up.license | 3 + .../no_leading_spaces_change_ipv6_pre_up | 8 + ...eading_spaces_change_ipv6_pre_up.exceptions.txt | 9 + ...paces_change_ipv6_pre_up.exceptions.txt.license | 3 + .../no_leading_spaces_change_ipv6_pre_up.json | 21 + ..._leading_spaces_change_ipv6_pre_up.json.license | 3 + .../no_leading_spaces_change_ipv6_pre_up.license | 3 + .../golden_output/no_leading_spaces_change_method | 8 + .../no_leading_spaces_change_method.exceptions.txt | 8 + ...ing_spaces_change_method.exceptions.txt.license | 3 + .../no_leading_spaces_change_method.json | 21 + .../no_leading_spaces_change_method.json.license | 3 + .../no_leading_spaces_change_method.license | 3 + .../golden_output/no_leading_spaces_revert | 7 + .../no_leading_spaces_revert.exceptions.txt | 0 ...no_leading_spaces_revert.exceptions.txt.license | 3 + .../golden_output/no_leading_spaces_revert.json | 21 + .../no_leading_spaces_revert.json.license | 3 + .../golden_output/no_leading_spaces_revert.license | 3 + .../no_leading_spaces_set_aggi_and_eth0_mtu | 8 + ...ing_spaces_set_aggi_and_eth0_mtu.exceptions.txt | 8 + ...es_set_aggi_and_eth0_mtu.exceptions.txt.license | 3 + .../no_leading_spaces_set_aggi_and_eth0_mtu.json | 21 + ...ading_spaces_set_aggi_and_eth0_mtu.json.license | 3 + ...no_leading_spaces_set_aggi_and_eth0_mtu.license | 3 + .../no_leading_spaces_set_aggi_slaves | 8 + ...o_leading_spaces_set_aggi_slaves.exceptions.txt | 8 + ...g_spaces_set_aggi_slaves.exceptions.txt.license | 3 + .../no_leading_spaces_set_aggi_slaves.json | 21 + .../no_leading_spaces_set_aggi_slaves.json.license | 3 + .../no_leading_spaces_set_aggi_slaves.license | 3 + .../golden_output/servers.com.test_no_changes | 61 + .../golden_output/servers.com.test_no_changes.json | 109 + .../servers.com.test_no_changes.json.license | 3 + .../servers.com.test_no_changes.license | 3 + .../golden_output/servers.com_add_aggi_up | 62 + .../servers.com_add_aggi_up.exceptions.txt | 0 .../golden_output/servers.com_add_aggi_up.json | 109 + .../servers.com_add_aggi_up.json.license | 3 + .../golden_output/servers.com_add_aggi_up.license | 3 + .../golden_output/servers.com_add_aggi_up_twice | 62 + .../servers.com_add_aggi_up_twice.exceptions.txt | 0 .../servers.com_add_aggi_up_twice.json | 109 + .../servers.com_add_aggi_up_twice.json.license | 3 + .../servers.com_add_aggi_up_twice.license | 3 + .../servers.com_add_and_delete_aggi_up | 61 + ...rvers.com_add_and_delete_aggi_up.exceptions.txt | 0 .../servers.com_add_and_delete_aggi_up.json | 109 + ...servers.com_add_and_delete_aggi_up.json.license | 3 + .../servers.com_add_and_delete_aggi_up.license | 3 + .../golden_output/servers.com_aggi_remove_dup | 62 + .../servers.com_aggi_remove_dup.exceptions.txt | 0 .../golden_output/servers.com_aggi_remove_dup.json | 109 + .../servers.com_aggi_remove_dup.json.license | 3 + .../servers.com_aggi_remove_dup.license | 3 + .../golden_output/servers.com_change_ipv4 | 61 + .../servers.com_change_ipv4.exceptions.txt | 9 + .../servers.com_change_ipv4.exceptions.txt.license | 3 + .../golden_output/servers.com_change_ipv4.json | 109 + .../servers.com_change_ipv4.json.license | 3 + .../golden_output/servers.com_change_ipv4.license | 3 + .../golden_output/servers.com_change_ipv4_post_up | 61 + .../servers.com_change_ipv4_post_up.exceptions.txt | 9 + ....com_change_ipv4_post_up.exceptions.txt.license | 3 + .../servers.com_change_ipv4_post_up.json | 109 + .../servers.com_change_ipv4_post_up.json.license | 3 + .../servers.com_change_ipv4_post_up.license | 3 + .../golden_output/servers.com_change_ipv4_pre_up | 61 + .../servers.com_change_ipv4_pre_up.exceptions.txt | 9 + ...s.com_change_ipv4_pre_up.exceptions.txt.license | 3 + .../servers.com_change_ipv4_pre_up.json | 109 + .../servers.com_change_ipv4_pre_up.json.license | 3 + .../servers.com_change_ipv4_pre_up.license | 3 + .../golden_output/servers.com_change_ipv6 | 61 + .../servers.com_change_ipv6.exceptions.txt | 9 + .../servers.com_change_ipv6.exceptions.txt.license | 3 + .../golden_output/servers.com_change_ipv6.json | 109 + .../servers.com_change_ipv6.json.license | 3 + .../golden_output/servers.com_change_ipv6.license | 3 + .../golden_output/servers.com_change_ipv6_post_up | 61 + .../servers.com_change_ipv6_post_up.exceptions.txt | 9 + ....com_change_ipv6_post_up.exceptions.txt.license | 3 + .../servers.com_change_ipv6_post_up.json | 109 + .../servers.com_change_ipv6_post_up.json.license | 3 + .../servers.com_change_ipv6_post_up.license | 3 + .../golden_output/servers.com_change_ipv6_pre_up | 61 + .../servers.com_change_ipv6_pre_up.exceptions.txt | 9 + ...s.com_change_ipv6_pre_up.exceptions.txt.license | 3 + .../servers.com_change_ipv6_pre_up.json | 109 + .../servers.com_change_ipv6_pre_up.json.license | 3 + .../servers.com_change_ipv6_pre_up.license | 3 + .../golden_output/servers.com_change_method | 61 + .../servers.com_change_method.exceptions.txt | 0 .../golden_output/servers.com_change_method.json | 109 + .../servers.com_change_method.json.license | 3 + .../servers.com_change_method.license | 3 + .../golden_output/servers.com_revert | 61 + .../servers.com_revert.exceptions.txt | 8 + .../servers.com_revert.exceptions.txt.license | 3 + .../golden_output/servers.com_revert.json | 109 + .../golden_output/servers.com_revert.json.license | 3 + .../golden_output/servers.com_revert.license | 3 + .../servers.com_set_aggi_and_eth0_mtu | 61 + ...ervers.com_set_aggi_and_eth0_mtu.exceptions.txt | 8 + ...om_set_aggi_and_eth0_mtu.exceptions.txt.license | 3 + .../servers.com_set_aggi_and_eth0_mtu.json | 109 + .../servers.com_set_aggi_and_eth0_mtu.json.license | 3 + .../servers.com_set_aggi_and_eth0_mtu.license | 3 + .../golden_output/servers.com_set_aggi_slaves | 61 + .../servers.com_set_aggi_slaves.exceptions.txt | 0 .../golden_output/servers.com_set_aggi_slaves.json | 109 + .../servers.com_set_aggi_slaves.json.license | 3 + .../servers.com_set_aggi_slaves.license | 3 + .../golden_output/up_down_dup.test_no_changes | 11 + .../golden_output/up_down_dup.test_no_changes.json | 24 + .../up_down_dup.test_no_changes.json.license | 3 + .../up_down_dup.test_no_changes.license | 3 + .../golden_output/up_down_dup_add_aggi_up | 11 + .../up_down_dup_add_aggi_up.exceptions.txt | 0 .../golden_output/up_down_dup_add_aggi_up.json | 24 + .../up_down_dup_add_aggi_up.json.license | 3 + .../golden_output/up_down_dup_add_aggi_up.license | 3 + .../golden_output/up_down_dup_add_aggi_up_twice | 11 + .../up_down_dup_add_aggi_up_twice.exceptions.txt | 0 .../up_down_dup_add_aggi_up_twice.json | 24 + .../up_down_dup_add_aggi_up_twice.json.license | 3 + .../up_down_dup_add_aggi_up_twice.license | 3 + .../up_down_dup_add_and_delete_aggi_up | 9 + ..._down_dup_add_and_delete_aggi_up.exceptions.txt | 0 .../up_down_dup_add_and_delete_aggi_up.json | 24 + ...up_down_dup_add_and_delete_aggi_up.json.license | 3 + .../up_down_dup_add_and_delete_aggi_up.license | 3 + .../golden_output/up_down_dup_aggi_remove_dup | 10 + .../up_down_dup_aggi_remove_dup.exceptions.txt | 0 .../golden_output/up_down_dup_aggi_remove_dup.json | 24 + .../up_down_dup_aggi_remove_dup.json.license | 3 + .../up_down_dup_aggi_remove_dup.license | 3 + .../golden_output/up_down_dup_change_ipv4 | 11 + .../up_down_dup_change_ipv4.exceptions.txt | 9 + .../up_down_dup_change_ipv4.exceptions.txt.license | 3 + .../golden_output/up_down_dup_change_ipv4.json | 24 + .../up_down_dup_change_ipv4.json.license | 3 + .../golden_output/up_down_dup_change_ipv4.license | 3 + .../golden_output/up_down_dup_change_ipv4_post_up | 11 + .../up_down_dup_change_ipv4_post_up.exceptions.txt | 9 + ..._dup_change_ipv4_post_up.exceptions.txt.license | 3 + .../up_down_dup_change_ipv4_post_up.json | 24 + .../up_down_dup_change_ipv4_post_up.json.license | 3 + .../up_down_dup_change_ipv4_post_up.license | 3 + .../golden_output/up_down_dup_change_ipv4_pre_up | 11 + .../up_down_dup_change_ipv4_pre_up.exceptions.txt | 9 + ...n_dup_change_ipv4_pre_up.exceptions.txt.license | 3 + .../up_down_dup_change_ipv4_pre_up.json | 24 + .../up_down_dup_change_ipv4_pre_up.json.license | 3 + .../up_down_dup_change_ipv4_pre_up.license | 3 + .../golden_output/up_down_dup_change_ipv6 | 11 + .../up_down_dup_change_ipv6.exceptions.txt | 9 + .../up_down_dup_change_ipv6.exceptions.txt.license | 3 + .../golden_output/up_down_dup_change_ipv6.json | 24 + .../up_down_dup_change_ipv6.json.license | 3 + .../golden_output/up_down_dup_change_ipv6.license | 3 + .../golden_output/up_down_dup_change_ipv6_post_up | 11 + .../up_down_dup_change_ipv6_post_up.exceptions.txt | 9 + ..._dup_change_ipv6_post_up.exceptions.txt.license | 3 + .../up_down_dup_change_ipv6_post_up.json | 24 + .../up_down_dup_change_ipv6_post_up.json.license | 3 + .../up_down_dup_change_ipv6_post_up.license | 3 + .../golden_output/up_down_dup_change_ipv6_pre_up | 11 + .../up_down_dup_change_ipv6_pre_up.exceptions.txt | 9 + ...n_dup_change_ipv6_pre_up.exceptions.txt.license | 3 + .../up_down_dup_change_ipv6_pre_up.json | 24 + .../up_down_dup_change_ipv6_pre_up.json.license | 3 + .../up_down_dup_change_ipv6_pre_up.license | 3 + .../golden_output/up_down_dup_change_method | 11 + .../up_down_dup_change_method.exceptions.txt | 8 + ...p_down_dup_change_method.exceptions.txt.license | 3 + .../golden_output/up_down_dup_change_method.json | 24 + .../up_down_dup_change_method.json.license | 3 + .../up_down_dup_change_method.license | 3 + .../golden_output/up_down_dup_revert | 11 + .../up_down_dup_revert.exceptions.txt | 8 + .../up_down_dup_revert.exceptions.txt.license | 3 + .../golden_output/up_down_dup_revert.json | 24 + .../golden_output/up_down_dup_revert.json.license | 3 + .../golden_output/up_down_dup_revert.license | 3 + .../up_down_dup_set_aggi_and_eth0_mtu | 11 + ...p_down_dup_set_aggi_and_eth0_mtu.exceptions.txt | 8 + ...up_set_aggi_and_eth0_mtu.exceptions.txt.license | 3 + .../up_down_dup_set_aggi_and_eth0_mtu.json | 24 + .../up_down_dup_set_aggi_and_eth0_mtu.json.license | 3 + .../up_down_dup_set_aggi_and_eth0_mtu.license | 3 + .../golden_output/up_down_dup_set_aggi_slaves | 12 + .../up_down_dup_set_aggi_slaves.exceptions.txt | 0 .../golden_output/up_down_dup_set_aggi_slaves.json | 24 + .../up_down_dup_set_aggi_slaves.json.license | 3 + .../up_down_dup_set_aggi_slaves.license | 3 + .../interfaces_file_fixtures/input/address_family | 12 + .../input/address_family.license | 3 + .../interfaces_file_fixtures/input/default_dhcp | 6 + .../input/default_dhcp.license | 3 + .../input/no_leading_spaces | 8 + .../input/no_leading_spaces.license | 3 + .../interfaces_file_fixtures/input/servers.com | 61 + .../input/servers.com.license | 3 + .../interfaces_file_fixtures/input/up_down_dup | 11 + .../input/up_down_dup.license | 3 + .../interfaces_file/test_interfaces_file.py | 557 +++ .../tests/unit/plugins/modules/linode_conftest.py | 87 + .../tests/unit/plugins/modules/oneview_conftest.py | 28 + .../unit/plugins/modules/oneview_module_loader.py | 36 + .../tests/unit/plugins/modules/rhn_conftest.py | 35 + .../unit/plugins/modules/test_alerta_customer.py | 250 ++ .../unit/plugins/modules/test_apache2_module.py | 24 + .../general/tests/unit/plugins/modules/test_apk.py | 38 + .../tests/unit/plugins/modules/test_archive.py | 75 + .../plugins/modules/test_bitbucket_access_key.py | 343 ++ .../modules/test_bitbucket_pipeline_key_pair.py | 198 + .../modules/test_bitbucket_pipeline_known_host.py | 193 + .../modules/test_bitbucket_pipeline_variable.py | 311 ++ .../tests/unit/plugins/modules/test_campfire.py | 96 + .../plugins/modules/test_circonus_annotation.py | 153 + .../tests/unit/plugins/modules/test_cpanm.py | 293 ++ .../modules/test_datadog_downtime.py.disabled | 226 ++ .../tests/unit/plugins/modules/test_dconf.py | 44 + .../tests/unit/plugins/modules/test_discord.py | 105 + .../tests/unit/plugins/modules/test_dnsimple.py | 64 + .../unit/plugins/modules/test_dnsimple_info.py | 111 + .../tests/unit/plugins/modules/test_gconftool2.py | 116 + .../unit/plugins/modules/test_gconftool2_info.py | 103 + .../general/tests/unit/plugins/modules/test_gem.py | 143 + .../tests/unit/plugins/modules/test_github_repo.py | 330 ++ .../unit/plugins/modules/test_gitlab_deploy_key.py | 109 + .../unit/plugins/modules/test_gitlab_group.py | 133 + .../tests/unit/plugins/modules/test_gitlab_hook.py | 104 + .../unit/plugins/modules/test_gitlab_project.py | 125 + .../modules/test_gitlab_protected_branch.py | 83 + .../unit/plugins/modules/test_gitlab_runner.py | 145 + .../tests/unit/plugins/modules/test_gitlab_user.py | 184 + .../tests/unit/plugins/modules/test_hana_query.py | 103 + .../tests/unit/plugins/modules/test_homebrew.py | 24 + .../unit/plugins/modules/test_homebrew_cask.py | 23 + .../unit/plugins/modules/test_icinga2_feature.py | 100 + .../unit/plugins/modules/test_ipa_otpconfig.py | 407 ++ .../unit/plugins/modules/test_ipa_otptoken.py | 496 +++ .../unit/plugins/modules/test_ipa_pwpolicy.py | 614 +++ .../unit/plugins/modules/test_java_keystore.py | 422 ++ .../unit/plugins/modules/test_jenkins_build.py | 224 + .../unit/plugins/modules/test_jenkins_plugin.py | 192 + .../modules/test_keycloak_authentication.py | 623 +++ .../unit/plugins/modules/test_keycloak_client.py | 150 + .../modules/test_keycloak_client_rolemapping.py | 573 +++ .../plugins/modules/test_keycloak_clientscope.py | 614 +++ .../modules/test_keycloak_identity_provider.py | 588 +++ .../unit/plugins/modules/test_keycloak_realm.py | 311 ++ .../plugins/modules/test_keycloak_realm_info.py | 122 + .../unit/plugins/modules/test_keycloak_role.py | 327 ++ .../modules/test_keycloak_user_federation.py | 582 +++ .../tests/unit/plugins/modules/test_linode.py | 22 + .../tests/unit/plugins/modules/test_linode_v4.py | 379 ++ .../tests/unit/plugins/modules/test_lxca_cmms.py | 101 + .../tests/unit/plugins/modules/test_lxca_nodes.py | 103 + .../tests/unit/plugins/modules/test_macports.py | 35 + .../unit/plugins/modules/test_maven_artifact.py | 71 + .../tests/unit/plugins/modules/test_modprobe.py | 485 +++ .../tests/unit/plugins/modules/test_monit.py | 159 + .../tests/unit/plugins/modules/test_nmcli.py | 4261 ++++++++++++++++++++ .../general/tests/unit/plugins/modules/test_npm.py | 262 ++ .../unit/plugins/modules/test_ocapi_command.py | 639 +++ .../tests/unit/plugins/modules/test_ocapi_info.py | 240 ++ .../tests/unit/plugins/modules/test_one_vm.py | 60 + .../modules/test_oneview_datacenter_info.py | 80 + .../plugins/modules/test_oneview_enclosure_info.py | 137 + .../modules/test_oneview_ethernet_network.py | 392 ++ .../modules/test_oneview_ethernet_network_info.py | 104 + .../plugins/modules/test_oneview_fc_network.py | 170 + .../modules/test_oneview_fc_network_info.py | 61 + .../plugins/modules/test_oneview_fcoe_network.py | 168 + .../modules/test_oneview_fcoe_network_info.py | 63 + .../test_oneview_logical_interconnect_group.py | 261 ++ ...test_oneview_logical_interconnect_group_info.py | 63 + .../plugins/modules/test_oneview_network_set.py | 187 + .../modules/test_oneview_network_set_info.py | 121 + .../plugins/modules/test_oneview_san_manager.py | 243 ++ .../modules/test_oneview_san_manager_info.py | 72 + .../tests/unit/plugins/modules/test_opkg.py | 241 ++ .../tests/unit/plugins/modules/test_pacman.py | 1099 +++++ .../tests/unit/plugins/modules/test_pacman_key.py | 577 +++ .../tests/unit/plugins/modules/test_pagerduty.py | 130 + .../unit/plugins/modules/test_pagerduty_alert.py | 46 + .../unit/plugins/modules/test_pagerduty_change.py | 85 + .../tests/unit/plugins/modules/test_pamd.py | 386 ++ .../tests/unit/plugins/modules/test_parted.py | 346 ++ .../tests/unit/plugins/modules/test_pkgin.py | 145 + .../tests/unit/plugins/modules/test_pmem.py | 707 ++++ .../tests/unit/plugins/modules/test_pritunl_org.py | 205 + .../unit/plugins/modules/test_pritunl_org_info.py | 138 + .../unit/plugins/modules/test_pritunl_user.py | 209 + .../unit/plugins/modules/test_pritunl_user_info.py | 161 + .../tests/unit/plugins/modules/test_proxmox_kvm.py | 20 + .../unit/plugins/modules/test_proxmox_snap.py | 117 + .../plugins/modules/test_proxmox_tasks_info.py | 195 + .../tests/unit/plugins/modules/test_puppet.py | 227 ++ .../plugins/modules/test_redhat_subscription.py | 1337 ++++++ .../tests/unit/plugins/modules/test_redis_data.py | 278 ++ .../unit/plugins/modules/test_redis_data_incr.py | 208 + .../unit/plugins/modules/test_redis_data_info.py | 114 + .../tests/unit/plugins/modules/test_redis_info.py | 77 + .../tests/unit/plugins/modules/test_rhn_channel.py | 147 + .../unit/plugins/modules/test_rhn_register.py | 293 ++ .../unit/plugins/modules/test_rhsm_release.py | 148 + .../unit/plugins/modules/test_rpm_ostree_pkg.py | 108 + .../plugins/modules/test_sap_task_list_execute.py | 91 + .../unit/plugins/modules/test_sapcar_extract.py | 54 + .../test_scaleway_compute_private_network.py | 180 + .../modules/test_scaleway_private_network.py | 198 + .../tests/unit/plugins/modules/test_slack.py | 203 + .../unit/plugins/modules/test_solaris_zone.py | 116 + .../tests/unit/plugins/modules/test_ss_3par_cpg.py | 248 ++ .../tests/unit/plugins/modules/test_statsd.py | 101 + .../tests/unit/plugins/modules/test_sysupgrade.py | 69 + .../tests/unit/plugins/modules/test_terraform.py | 23 + .../general/tests/unit/plugins/modules/test_ufw.py | 477 +++ .../plugins/modules/test_wdc_redfish_command.py | 911 +++++ .../unit/plugins/modules/test_wdc_redfish_info.py | 216 + .../plugins/modules/test_xcc_redfish_command.py | 629 +++ .../plugins/modules/test_xenserver_guest_info.py | 79 + .../modules/test_xenserver_guest_powerstate.py | 299 ++ .../tests/unit/plugins/modules/test_xfconf.py | 312 ++ .../tests/unit/plugins/modules/test_xfconf_info.py | 172 + .../general/tests/unit/plugins/modules/utils.py | 54 + .../tests/unit/plugins/modules/xenserver_common.py | 12 + .../unit/plugins/modules/xenserver_conftest.py | 76 + .../community/general/tests/unit/requirements.txt | 46 + .../community/general/tests/unit/requirements.yml | 7 + .../community/general/tests/utils/constraints.txt | 59 + .../community/general/tests/utils/shippable/aix.sh | 29 + .../general/tests/utils/shippable/alpine.sh | 29 + .../general/tests/utils/shippable/fedora.sh | 29 + .../general/tests/utils/shippable/freebsd.sh | 29 + .../general/tests/utils/shippable/generic.sh | 21 + .../tests/utils/shippable/linux-community.sh | 22 + .../general/tests/utils/shippable/linux.sh | 21 + .../general/tests/utils/shippable/macos.sh | 29 + .../community/general/tests/utils/shippable/osx.sh | 29 + .../general/tests/utils/shippable/remote.sh | 29 + .../general/tests/utils/shippable/rhel.sh | 29 + .../general/tests/utils/shippable/sanity.sh | 45 + .../general/tests/utils/shippable/shippable.sh | 232 ++ .../general/tests/utils/shippable/ubuntu.sh | 29 + .../general/tests/utils/shippable/units.sh | 41 + 1877 files changed, 107650 insertions(+) create mode 100644 ansible_collections/community/general/tests/.gitignore create mode 100644 ansible_collections/community/general/tests/config.yml create mode 100644 ansible_collections/community/general/tests/integration/requirements.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/aix_devices/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/aix_devices/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/aix_filesystem/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alerta_customer/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/alerta_customer/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alerta_customer/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/path_is_checked.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/remove_links.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup_test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/subcommands.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_state.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_alternative create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_command create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/vars/Suse-42.3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/alternatives/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/files/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/apache2_module/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/635-apache2-misleading-warning.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/files/bar.txt create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/files/empty.txt create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/files/foo.txt create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/files/sub/subfile.txt create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/tests/broken-link.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/tests/exclusions.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/tests/idempotency.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_common_tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_filesystem_tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_filesystem_matching.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_clobber.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_error.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_skip.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_nested.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_recursive.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_simple.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_whitespace.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/callback/inventory.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/callback/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/callback_diy/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/callback_diy/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/callback_log_plays/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/callback_log_plays/ping_log.yml create mode 100755 ansible_collections/community/general/tests/integration/targets/callback_log_plays/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/callback_yaml/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/callback_yaml/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cargo/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/cargo/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_general.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_version.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cmd_runner/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py create mode 100644 ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/connection/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/connection/test.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/connection/test_connection.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_chroot/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/connection_chroot/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_chroot/test_connection.inventory create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_jail/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/connection_jail/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_jail/test_connection.inventory create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_lxc/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/connection_lxc/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_lxc/test_connection.inventory create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_lxd/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/connection_lxd/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_lxd/test_connection.inventory create mode 100644 ansible_collections/community/general/tests/integration/targets/connection_posix/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/connection_posix/test.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/consul/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/consul/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/copr/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/copr/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cpanm/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/cpanm/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cronvar/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/cronvar/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cronvar/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/cronvar/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/deploy_helper/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/deploy_helper/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/discord/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/discord/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/discord/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/discord/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/core/settings.py create mode 100755 ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/manage.py create mode 100755 ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/settings.py create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/urls.py create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/startproj/.keep create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml create mode 100644 ansible_collections/community/general/tests/integration/targets/dnf_versionlock/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/install.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_bash.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_updates.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dpkg_divert/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/prepare.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/01-basic.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/02-rename.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/etcd3/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/etcd3/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/etcd3/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/etcd3/tasks/run_tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/tasks/basics.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/tasks/errors.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/tasks/floats.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesize/tasks/symlinks.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_device.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_fs.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/freebsd_setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/remove_fs.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/vars/Ubuntu-14.04.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filesystem/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_counter/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_counter/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_dict/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_dict/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_dict_kv/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_dict_kv/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_from_csv/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_from_csv/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_from_csv/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/filter_hashids/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_hashids/runme.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_hashids/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_hashids/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_jc/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/filter_jc/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_jc/runme.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_jc/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_json_query/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/filter_json_query/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_json_query/runme.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_json_query/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_json_query/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_2-10.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_random_mac/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_random_mac/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_random_mac/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_time/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_time/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_version_sort/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/filter_version_sort/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/files/serve.py create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/tasks/check_mode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak_remote/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak_remote/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/check_mode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/record.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/remove_record.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gem/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gem/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gem/vars/FreeBSD.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gem/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gem/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/exclusion_state_list-all.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_no_state.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_no_value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_check_mode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_no_value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/git_config/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/github_issue/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/github_issue/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/github_issue/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_branch/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_branch/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_branch/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group_members/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group_members/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group_members/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group_variable/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_group_variable/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_hook/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_hook/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_hook/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_members/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_members/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_members/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_variable/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_project_variable/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_runner/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_runner/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_runner/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_user/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_user/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/sshkey.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hg/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hg/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hg/tasks/install.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hg/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hg/tasks/run-tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hg/tasks/uninstall.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/homebrew/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/homebrew_cask/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/homebrew_cask/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/homectl/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/homectl/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ilo_redfish_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ilo_redfish_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/influxdb_user/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/influxdb_user/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/01-value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/02-values.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/03-encoding.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/interfaces_file/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff create mode 100644 ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff_3841 create mode 100644 ansible_collections/community/general/tests/integration/targets/interfaces_file/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ipify_facts/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ipify_facts/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ipify_facts/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iptables_state/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/iptables_state/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/00-basic.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/10-rollback.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ipwcli_dns/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ipwcli_dns/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_create/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_create/files/test1.cfg create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_create/files/test_dir/test2.cfg create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_create/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_create/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_add_files.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_delete_files.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_mount.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/prepare.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso.license create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/7zip.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/prepare.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/FreeBSD.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Ubuntu.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/iso_extract/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12 create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12.license create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_keystore/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/java_keystore/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_keystore/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/prepare.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/jboss/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/jboss/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/jboss/tasks/jboss.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/jboss/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/jira/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/jira/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/jira/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/kdeconfig/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/kdeconfig/meta/main.yml create mode 100755 ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/files/kwriteconf_fake create mode 100644 ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/kernel_blacklist/files/blacklist create mode 100644 ansible_collections/community/general/tests/integration/targets/kernel_blacklist/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_client/docker-compose.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/docker-compose.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_group/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_group/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_group/readme.adoc create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_group/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_group/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_role/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keyring/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/keyring/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/keyring/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/files/ansible_test_service.py create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/teardown.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_reload.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_restart.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_runatload.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_start_stop.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unknown.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unload.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/templates/launchd.test.service.plist.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/templates/modified.launchd.test.service.plist.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/launchd/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ldap_search/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ldap_search/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/listen_ports_facts/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/listen_ports_facts/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/locale_gen/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/locale_gen.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_cartesian/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/galaxy.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json.license create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json.license create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/plugins/modules/collection_module.py create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nothing/plugins/modules/collection_module.py create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/galaxy.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/plugins/modules/collection_module.py create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/library/local_module.py create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_dependent/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_dig/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_dig/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/dependencies.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/meta/main.yml create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_etcd3/test_lookup_etcd3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_flattened/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/dependencies.yml create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test_db.py create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_with_env.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/vars.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/package.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input.license create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/security-privacy.repo.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_pet/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_pet/dependencies.yml create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_random_pet/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_pet/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_string/aliases create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_random_string/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_string/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_words/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_words/dependencies.yml create mode 100755 ansible_collections/community/general/tests/integration/targets/lookup_random_words/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/lookup_random_words/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_grow_reduce.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_indempotency.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/lxd_project/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/lxd_project/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mail/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.crt create mode 100644 ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.key create mode 100644 ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.py create mode 100644 ansible_collections/community/general/tests/integration/targets/mail/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mas/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_dns_reload/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_dns_reload/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_dns_reload/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_memstore_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_memstore_info/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_memstore_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_server_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_server_info/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_server_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_domain/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_domain/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_domain/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_domain/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_record/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_record/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_record/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/memset_zone_record/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/library/msimple.py create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/library/msimpleda.py create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mdepfail.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple_output_conflict.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimpleda.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mstate.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/files/httpd_echo.py create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/tasks/check_state.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/tasks/test_errors.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/tasks/test_reload_present.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/tasks/test_state.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/templates/monitrc.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/vars/Alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/vars/Archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/vars/CentOS-6.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/vars/Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/monit/vars/defaults.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mqtt/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/mqtt/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mqtt/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mqtt/tasks/ubuntu.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mssql_script/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/mssql_script/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mssql_script/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/nomad/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/nomad/files/job.hcl create mode 100644 ansible_collections/community/general/tests/integration/targets/nomad/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/nomad/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/nomad/tasks/nomad_job.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/tasks/no_bin_links.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/tasks/run.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/tasks/install_pyodbc.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/tasks/negative_tests.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/odbc/tasks/no_pyodbc.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/one_host/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/one_host/files/testhost/tmp/opennebula-fixtures.json.gz create mode 100644 ansible_collections/community/general/tests/integration/targets/one_host/files/testhost/tmp/opennebula-fixtures.json.gz.license create mode 100644 ansible_collections/community/general/tests/integration/targets/one_host/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/one_host/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/one_template/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/one_template/files/testhost/tmp/opennebula-fixtures.json.gz create mode 100644 ansible_collections/community/general/tests/integration/targets/one_template/files/testhost/tmp/opennebula-fixtures.json.gz.license create mode 100644 ansible_collections/community/general/tests/integration/targets/one_template/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/one_template/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/osx_defaults/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/osx_defaults/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/basic.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/package_urls.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/reason.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pacman/tasks/update_cache.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pagerduty_user/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pagerduty_user/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pagerduty_user/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pam_limits/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pam_limits/files/test_pam_limits.conf create mode 100644 ansible_collections/community/general/tests/integration/targets/pam_limits/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pamd/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pamd/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/parted/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/parted/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/parted/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pids/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pids/files/sleeper.c create mode 100644 ansible_collections/community/general/tests/integration/targets/pids/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/pipx/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pipx_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pipx_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/tasks/create-outofdate-pkg.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/tasks/install_single_package.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/tasks/setup-testjail.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2.license create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgng/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgutil/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/pkgutil/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/proxmox/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/python_requirements_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/python_requirements_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/read_csv/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/read_csv/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/read_csv/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/redis_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/redis_info/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/redis_info/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/redis_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/rundeck/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/rundeck/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/rundeck/files/test_job.yaml create mode 100644 ansible_collections/community/general/tests/integration/targets/rundeck/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/rundeck/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/ip.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/security_group.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/state.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_info/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_info/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_image_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_ip/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_ip/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_ip/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_lb/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_lb/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_lb/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_server_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_user_data/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_user_data/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_user_data/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_volume/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_volume/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_volume/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sefcontext/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/sefcontext/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/sefcontext.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_client/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_client/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/pipe.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/set.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/tcp.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/transport.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/udp.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/fedora.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/freebsd.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/redhat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_cron/vars/suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/README.md create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/D-Fedora.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Fedora.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Ubuntu.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_epel/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/RedHat-7.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/README.md create mode 100755 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/create-repo.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/files/repo.tar.xz create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/files/repo.tar.xz.license create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/handlers/main.yaml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/meta/main.yaml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/tasks/main.yaml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_gnutar/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_gnutar/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_influxdb/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_influxdb/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_mosquitto/files/mosquitto.conf create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_mosquitto/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/ubuntu.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif.license create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif.license create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Ubuntu.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_opennebula/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_opennebula/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_opennebula/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/CentOS-8.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Darwin.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/FreeBSD.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat-9.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control.license create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Alpine-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Archlinux-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-11-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-18-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-22-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_redis_replication/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_redis_replication/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_redis_replication/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/setup_redis_cluster.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default-cleanup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Alpine.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Archlinux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-Fedora.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.2.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.3.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.0.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.1.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-Ubuntu.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/default.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/nothing.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_tls/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/files/wildfly.conf create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/handlers/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/launch.sh.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/wildfly.service.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/shutdown/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/snap/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/snap/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/snap_alias/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/snap_alias/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ssh_config/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ssh_config/files/fake_id_rsa create mode 100644 ansible_collections/community/general/tests/integration/targets/ssh_config/files/ssh_config_test create mode 100644 ansible_collections/community/general/tests/integration/targets/ssh_config/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sudoers/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/files/sendProcessStdin.py create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Darwin.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_FreeBSD.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Linux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_pip.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/start_supervisord.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/stop_supervisord.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_start.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_stop.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Darwin.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_FreeBSD.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Linux.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_RedHat.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Suse.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_pip.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/templates/supervisord.conf create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/Debian.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/defaults.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sysrc/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/sysrc/tasks/setup-testjail.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/.gitignore create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/main.tf create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/variables.tf create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/tasks/complex_variables.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/tasks/test_provider_upgrade.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/templates/provider_test/main.tf.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/terraform/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/test_a_module/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/galaxy.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py create mode 100644 ansible_collections/community/general/tests/integration/targets/test_a_module/library/local_module.py create mode 100755 ansible_collections/community/general/tests/integration/targets/test_a_module/runme.sh create mode 100644 ansible_collections/community/general/tests/integration/targets/test_a_module/runme.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/timezone/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/timezone/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/timezone/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/tasks/run-test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/basic.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/global-state.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/insert_relative_to.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/interface.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/wakeonlan/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/wakeonlan/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xattr/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/xattr/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xattr/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xattr/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xattr/tasks/setup.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xattr/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/defaults/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/gquota.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/pquota.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/uquota.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml.license create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-children-elements-xml.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print-only.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/tasks/test-xmlstring.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/xml/vars/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/yarn/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/yarn/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/yarn/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/yarn/tasks/run.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/yarn/templates/package.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/yum_versionlock/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/yum_versionlock/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec.license create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/tasks/zypper.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper/templates/duplicate.spec.j2 create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper_repository/aliases create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper_repository/files/systemsmanagement_Uyuni_Utils.repo create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper_repository/meta/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/main.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/test.yml create mode 100644 ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml create mode 100644 ansible_collections/community/general/tests/sanity/extra/aliases.json create mode 100644 ansible_collections/community/general/tests/sanity/extra/aliases.json.license create mode 100755 ansible_collections/community/general/tests/sanity/extra/aliases.py create mode 100644 ansible_collections/community/general/tests/sanity/extra/botmeta.json create mode 100644 ansible_collections/community/general/tests/sanity/extra/botmeta.json.license create mode 100755 ansible_collections/community/general/tests/sanity/extra/botmeta.py create mode 100644 ansible_collections/community/general/tests/sanity/extra/extra-docs.json create mode 100644 ansible_collections/community/general/tests/sanity/extra/extra-docs.json.license create mode 100755 ansible_collections/community/general/tests/sanity/extra/extra-docs.py create mode 100644 ansible_collections/community/general/tests/sanity/extra/licenses.json create mode 100644 ansible_collections/community/general/tests/sanity/extra/licenses.json.license create mode 100755 ansible_collections/community/general/tests/sanity/extra/licenses.py create mode 100644 ansible_collections/community/general/tests/sanity/extra/licenses.py.license create mode 100644 ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json create mode 100644 ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json.license create mode 100755 ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.py create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.11.txt create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.11.txt.license create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.12.txt create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.12.txt.license create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.13.txt create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.13.txt.license create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.14.txt create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.14.txt.license create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.15.txt create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.15.txt.license create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.16.txt create mode 100644 ansible_collections/community/general/tests/sanity/ignore-2.16.txt.license create mode 100644 ansible_collections/community/general/tests/unit/compat/__init__.py create mode 100644 ansible_collections/community/general/tests/unit/compat/builtins.py create mode 100644 ansible_collections/community/general/tests/unit/compat/mock.py create mode 100644 ansible_collections/community/general/tests/unit/compat/unittest.py create mode 100644 ansible_collections/community/general/tests/unit/mock/loader.py create mode 100644 ansible_collections/community/general/tests/unit/mock/path.py create mode 100644 ansible_collections/community/general/tests/unit/mock/procenv.py create mode 100644 ansible_collections/community/general/tests/unit/mock/vault_helper.py create mode 100644 ansible_collections/community/general/tests/unit/mock/yaml_helper.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/helper.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/test_doas.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/test_dzdo.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/test_ksu.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/test_pbrun.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/test_pfexec.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/become/test_sudosu.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/cache/test_memcached.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/cache/test_redis.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/callback/test_elastic.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/callback/test_opentelemetry.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/callback/test_splunk.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/filter/test_crc32.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_cobbler.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_lxd.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_opennebula.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_stackpath_compute.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/inventory/test_xen_orchestra.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_dependent.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_dsv.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_etcd3.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_lastpass.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_manifold.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_revbitspss.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/lookup/test_tss.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_backoff.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_scaleway.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_dict_comparison.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_connect.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/net_tools/pritunl/test_api.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_csv.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_database.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_known_hosts.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_ocapi_utils.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_onepassword.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_opennebula.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_saslprep.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/test_utm_utils.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeAnsibleModule.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeXenAPI.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/common.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_gather_vm_params_and_facts.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_get_object_ref.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_misc.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_netaddr_functions.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_set_vm_power_state.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_wait_for_functions.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xapi.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xenserverobject.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/FakeAnsibleModule.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/FakeXenAPI.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/hpe_test_utils.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file-README.md create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6 create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.exceptions.txt create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup.license create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/test_interfaces_file.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/linode_conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/oneview_conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/oneview_module_loader.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/rhn_conftest.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_alerta_customer.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_apache2_module.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_apk.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_archive.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_access_key.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_key_pair.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_known_host.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_variable.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_campfire.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_circonus_annotation.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_datadog_downtime.py.disabled create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_dconf.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_discord.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_github_repo.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_deploy_key.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_hook.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_protected_branch.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_runner.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_user.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_hana_query.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew_cask.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_icinga2_feature.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otpconfig.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_java_keystore.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_plugin.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_clientscope.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_identity_provider.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_realm.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_realm_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_linode.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_linode_v4.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_cmms.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_nodes.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_macports.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_maven_artifact.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_monit.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_command.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_one_vm.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_datacenter_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_enclosure_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pacman.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pacman_key.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_change.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pamd.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_parted.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pmem.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_incr.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_channel.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_register.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_rpm_ostree_pkg.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_sap_task_list_execute.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_sapcar_extract.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_compute_private_network.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_private_network.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_solaris_zone.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ss_3par_cpg.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_statsd.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_sysupgrade.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_terraform.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_ufw.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_command.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_xcc_redfish_command.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_powerstate.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/utils.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/xenserver_common.py create mode 100644 ansible_collections/community/general/tests/unit/plugins/modules/xenserver_conftest.py create mode 100644 ansible_collections/community/general/tests/unit/requirements.txt create mode 100644 ansible_collections/community/general/tests/unit/requirements.yml create mode 100644 ansible_collections/community/general/tests/utils/constraints.txt create mode 100755 ansible_collections/community/general/tests/utils/shippable/aix.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/alpine.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/fedora.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/freebsd.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/generic.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/linux-community.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/linux.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/macos.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/osx.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/remote.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/rhel.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/sanity.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/shippable.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/ubuntu.sh create mode 100755 ansible_collections/community/general/tests/utils/shippable/units.sh (limited to 'ansible_collections/community/general/tests') diff --git a/ansible_collections/community/general/tests/.gitignore b/ansible_collections/community/general/tests/.gitignore new file mode 100644 index 000000000..6edf5dc10 --- /dev/null +++ b/ansible_collections/community/general/tests/.gitignore @@ -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 + +output/ diff --git a/ansible_collections/community/general/tests/config.yml b/ansible_collections/community/general/tests/config.yml new file mode 100644 index 000000000..38590f2e4 --- /dev/null +++ b/ansible_collections/community/general/tests/config.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 + +# See template for more information: +# https://github.com/ansible/ansible/blob/devel/test/lib/ansible_test/config/config.yml +modules: + python_requires: default diff --git a/ansible_collections/community/general/tests/integration/requirements.yml b/ansible_collections/community/general/tests/integration/requirements.yml new file mode 100644 index 000000000..b772fc82d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/requirements.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 + +collections: +- ansible.posix +- community.crypto +- community.docker diff --git a/ansible_collections/community/general/tests/integration/targets/aix_devices/aliases b/ansible_collections/community/general/tests/integration/targets/aix_devices/aliases new file mode 100644 index 000000000..8d841c56b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/aix_devices/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 + +# No AIX LPAR available +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/aix_devices/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/aix_devices/tasks/main.yml new file mode 100644 index 000000000..284f46c33 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/aix_devices/tasks/main.yml @@ -0,0 +1,81 @@ +--- +#################################################################### +# 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: Scan new devices. + aix_devices: + device: all + state: present + +- name: Scan new virtual devices (vio0). + aix_devices: + device: vio0 + state: present + +- name: Removing IP alias to en0 + aix_devices: + device: en0 + attributes: + delalias4: 10.0.0.100,255.255.255.0 + +- name: Removes ent2. + aix_devices: + device: ent2 + state: absent + +- name: Put device en2 in Defined + aix_devices: + device: en2 + state: defined + +- name: Removes ent4 (inexistent). + aix_devices: + device: ent4 + state: absent + +- name: Put device en4 in Defined (inexistent) + aix_devices: + device: en4 + state: defined + +- name: Put vscsi1 and children devices in Defined state. + aix_devices: + device: vscsi1 + recursive: true + state: defined + +- name: Removes vscsi1 and children devices. + aix_devices: + device: vscsi1 + recursive: true + state: absent + +- name: Changes en1 mtu to 9000 and disables arp. + aix_devices: + device: en1 + attributes: + mtu: 900 + arp: 'off' + state: present + +- name: Configure IP, netmask and set en1 up. + aix_devices: + device: en1 + attributes: + netaddr: 192.168.0.100 + netmask: 255.255.255.0 + state: up + state: present + +- name: Adding IP alias to en0 + aix_devices: + device: en0 + attributes: + alias4: 10.0.0.100,255.255.255.0 + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/aix_filesystem/aliases b/ansible_collections/community/general/tests/integration/targets/aix_filesystem/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/aix_filesystem/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/aix_filesystem/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml new file mode 100644 index 000000000..25146062d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml @@ -0,0 +1,130 @@ +--- +#################################################################### +# 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: Umounting /testfs + aix_filesystem: + filesystem: /testfs + state: unmounted + +- name: Removing /testfs + aix_filesystem: + filesystem: /testfs + state: absent + +- name: Creating a new file system + aix_filesystem: + filesystem: /newfs + size: 1G + state: present + vg: datavg + +# It requires a host (nfshost) exporting the NFS +- name: Creating NFS filesystem from nfshost (Linux NFS server) + aix_filesystem: + device: /home/ftp + nfs_server: nfshost + filesystem: /nfs/ftp + state: present + +# It requires a volume group named datavg (next three actions) +- name: Creating a logical volume testlv (aix_lvol module) + aix_lvol: + vg: datavg + lv: testlv + size: 2G + state: present + +- name: Create filesystem in a previously defined logical volume + aix_filesystem: + device: testlv + filesystem: /testfs + state: present + +- name: Create an already existing filesystem using existing logical volume. + aix_filesystem: + vg: datavg + device: mksysblv + filesystem: /mksysb + state: present + +- name: Create a filesystem in a non-existing VG + aix_filesystem: + vg: nonexistvg + filesystem: /newlv + state: present + +- name: Resizing /mksysb to 1G + aix_filesystem: + filesystem: /mksysb + size: 1G + state: present + +- name: Resizing /mksysb to +512M + aix_filesystem: + filesystem: /mksysb + size: +512M + state: present + +- name: Resizing /mksysb to 11G + aix_filesystem: + filesystem: /mksysb + size: 11G + state: present + +- name: Resizing /mksysb to 11G (already done) + aix_filesystem: + filesystem: /mksysb + size: 11G + state: present + +- name: Resizing /mksysb to -2G + aix_filesystem: + filesystem: /mksysb + size: -2G + state: present + +- name: Resizing /mksysb to 100G (no enought space) + aix_filesystem: + filesystem: /mksysb + size: +100G + state: present + +- name: Unmount filesystem /home/ftp + aix_filesystem: + filesystem: /home/ftp + state: unmounted + +- name: Remove NFS filesystem /home/ftp + aix_filesystem: + filesystem: /home/ftp + rm_mount_point: true + state: absent + +- name: Mount filesystem /newfs + aix_filesystem: + filesystem: /newfs + state: mounted + +- name: Remove mounted /newfs + aix_filesystem: + filesystem: /newfs + rm_mount_point: true + state: absent + +- name: Umount /newfs + aix_filesystem: + filesystem: /newfs + state: unmounted + +- name: Remove /newfs + aix_filesystem: + filesystem: /newfs + rm_mount_point: true + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/alerta_customer/aliases b/ansible_collections/community/general/tests/integration/targets/alerta_customer/aliases new file mode 100644 index 000000000..d163e8d9c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alerta_customer/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/1 +disabled diff --git a/ansible_collections/community/general/tests/integration/targets/alerta_customer/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/alerta_customer/defaults/main.yml new file mode 100644 index 000000000..3d4877b41 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alerta_customer/defaults/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 + +alerta_url: http://localhost:8080/ +alerta_user: admin@example.com +alerta_password: password +alerta_key: demo-key diff --git a/ansible_collections/community/general/tests/integration/targets/alerta_customer/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/alerta_customer/tasks/main.yml new file mode 100644 index 000000000..b91c24b53 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alerta_customer/tasks/main.yml @@ -0,0 +1,156 @@ +--- +#################################################################### +# 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: Create customer (check mode) + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + check_mode: true + register: result + +- name: Check result (check mode) + assert: + that: + - result is changed + +- name: Create customer + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + register: result + +- name: Check customer creation + assert: + that: + - result is changed + +- name: Test customer creation idempotency + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + register: result + +- name: Check customer creation idempotency + assert: + that: + - result is not changed + +- name: Delete customer (check mode) + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + state: absent + check_mode: true + register: result + +- name: Check customer deletion (check mode) + assert: + that: + - result is changed + +- name: Delete customer + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + state: absent + register: result + +- name: Check customer deletion + assert: + that: + - result is changed + +- name: Test customer deletion idempotency + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + state: absent + register: result + +- name: Check customer deletion idempotency + assert: + that: + - result is not changed + +- name: Delete non-existing customer (check mode) + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_username: "{{ alerta_user }}" + api_password: "{{ alerta_password }}" + customer: customer1 + match: admin@admin.admin + state: absent + check_mode: true + register: result + +- name: Check non-existing customer deletion (check mode) + assert: + that: + - result is not changed + +- name: Create customer with api key + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_key: "{{ alerta_key }}" + customer: customer1 + match: admin@admin.admin + register: result + +- name: Check customer creation with api key + assert: + that: + - result is changed + +- name: Delete customer with api key + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_key: "{{ alerta_key }}" + customer: customer1 + match: admin@admin.admin + state: absent + register: result + +- name: Check customer deletion with api key + assert: + that: + - result is changed + +- name: Use wrong api key + alerta_customer: + alerta_url: "{{ alerta_url }}" + api_key: wrong_key + customer: customer1 + match: admin@admin.admin + register: result + ignore_errors: true + +- name: Check customer creation with api key + assert: + that: + - result is not changed + - result is failed diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/aliases b/ansible_collections/community/general/tests/integration/targets/alternatives/aliases new file mode 100644 index 000000000..f360ac626 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/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 +destructive +needs/root +skip/aix +skip/freebsd +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/main.yml new file mode 100644 index 000000000..81d6a7b0d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/main.yml @@ -0,0 +1,93 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2017 Pierre-Louis Bonicoli +# 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: create a dummy alternative' + block: + - import_tasks: setup.yml + + ############## + # Test parameters: + # link parameter present / absent ('with_link' variable) + # with / without alternatives defined in alternatives file ('with_alternatives' variable) + # auto / manual ('mode' variable) + + - include_tasks: tests.yml + with_nested: + - [ true, false ] # with_link + - [ true, false ] # with_alternatives + - [ 'auto', 'manual' ] # mode + loop_control: + loop_var: test_conf + + ########## + # Priority + - block: + - include_tasks: remove_links.yml + - include_tasks: setup_test.yml + # at least two iterations again + - include_tasks: tests_set_priority.yml + with_sequence: start=3 end=4 + vars: + with_alternatives: true + mode: auto + + - block: + - include_tasks: remove_links.yml + - include_tasks: setup_test.yml + # at least two iterations again + - include_tasks: tests_set_priority.yml + with_sequence: start=3 end=4 + vars: + with_alternatives: false + mode: auto + + # Test that path is checked: alternatives must fail when path is nonexistent + - import_tasks: path_is_checked.yml + + # Test that subcommands commands work + - import_tasks: subcommands.yml + + # Test operation of the 'state' parameter + - block: + - include_tasks: remove_links.yml + - include_tasks: tests_state.yml + + # Cleanup + always: + - include_tasks: remove_links.yml + + - file: + path: '{{ item }}' + state: absent + with_items: + - '{{ alternatives_dir }}/dummy' + - '{{ alternatives_dir }}/dummymain' + - '{{ alternatives_dir }}/dummysubcmd' + + - file: + path: '/usr/bin/dummy{{ item }}' + state: absent + with_sequence: start=1 end=4 + + # *Disable tests on Fedora 24* + # Shippable Fedora 24 image provides chkconfig-1.7-2.fc24.x86_64 but not the + # latest available version (chkconfig-1.8-1.fc24.x86_64). update-alternatives + # in chkconfig-1.7-2 fails when /etc/alternatives/dummy link is missing, + # error is: 'failed to read link /usr/bin/dummy: No such file or directory'. + # Moreover Fedora 24 is no longer maintained. + # + # *Disable tests on Arch Linux* + # TODO: figure out whether there is an alternatives tool for Arch Linux + # + # *Disable tests on Alpine* + # TODO: figure out whether there is an alternatives tool for Alpine + when: + - ansible_distribution != 'Fedora' or ansible_distribution_major_version|int > 24 + - ansible_distribution != 'Archlinux' + - ansible_distribution != 'Alpine' diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/path_is_checked.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/path_is_checked.yml new file mode 100644 index 000000000..0bc435889 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/path_is_checked.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 + +- name: Try with nonexistent path + alternatives: + name: dummy + path: '/non/existent/path/there' + link: '/usr/bin/dummy' + ignore_errors: true + register: alternative + +- name: Check previous task failed + assert: + that: + - 'alternative is failed' diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/remove_links.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/remove_links.yml new file mode 100644 index 000000000..de25b02cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/remove_links.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 + +- name: remove links + file: + path: '{{ item }}' + state: absent + with_items: + - "{{ alternatives_dir }}/dummy" + - /etc/alternatives/dummy + - /usr/bin/dummy diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup.yml new file mode 100644 index 000000000..ab2c39852 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup.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_vars: '{{ item }}' + with_first_found: + - files: + - '{{ ansible_os_family }}-{{ ansible_distribution_version }}.yml' + - '{{ ansible_os_family }}.yml' + - default.yml + paths: ../vars +- template: + src: dummy_command + dest: /usr/bin/dummy{{ item }} + owner: root + group: root + mode: '0755' + with_sequence: start=1 end=4 diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup_test.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup_test.yml new file mode 100644 index 000000000..77279c67f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/setup_test.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 + +- template: + src: dummy_alternative + dest: '{{ alternatives_dir }}/dummy' + owner: root + group: root + mode: '0644' + when: with_alternatives or ansible_os_family != 'RedHat' +- file: + path: '{{ alternatives_dir }}/dummy' + state: absent + when: not with_alternatives and ansible_os_family == 'RedHat' diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/subcommands.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/subcommands.yml new file mode 100644 index 000000000..678bbe68f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/subcommands.yml @@ -0,0 +1,222 @@ +--- +# 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: Try with subcommands + alternatives: + name: dummymain + path: '/usr/bin/dummy1' + link: '/usr/bin/dummymain' + subcommands: + - name: dummysubcmd + path: '/usr/bin/dummy2' + link: '/usr/bin/dummysubcmd' + register: alternative + +- name: Check expected command was executed + assert: + that: + - 'alternative is changed' + +- name: Execute the current dummymain command + command: dummymain + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy1" + +- name: Execute the current dummysubcmd command + command: dummysubcmd + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy2" + +- name: Get dummymain alternatives output + command: + cmd: '{{ alternatives_command }} --display dummymain' + register: result + +- name: Print result + debug: + var: result.stdout_lines + +- name: Subcommands are not removed if not specified + alternatives: + name: dummymain + path: '/usr/bin/dummy1' + link: '/usr/bin/dummymain' + register: alternative + +- name: Check expected command was executed + assert: + that: + - 'alternative is not changed' + +- name: Execute the current dummysubcmd command + command: dummysubcmd + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy2" + +- name: Subcommands are removed if set to an empty list + alternatives: + name: dummymain + path: '/usr/bin/dummy1' + link: '/usr/bin/dummymain' + subcommands: [] + register: alternative + +- name: Check expected command was executed + assert: + that: + - 'alternative is changed' + +- name: Execute the current dummysubcmd command + command: dummysubcmd + register: cmd + ignore_errors: true + +- name: Ensure that the subcommand is gone + assert: + that: + - cmd.rc == 2 + - '"No such file" in cmd.msg' + +- name: Get dummymain alternatives output + command: + cmd: '{{ alternatives_command }} --display dummymain' + register: result + +- name: Print result + debug: + var: result.stdout_lines + +- name: Install other alternative with subcommands + alternatives: + name: dummymain + path: '/usr/bin/dummy3' + link: '/usr/bin/dummymain' + subcommands: + - name: dummysubcmd + path: '/usr/bin/dummy4' + link: '/usr/bin/dummysubcmd' + register: alternative + +- name: Check expected command was executed + assert: + that: + - 'alternative is changed' + +- name: Execute the current dummymain command + command: dummymain + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy3" + +- name: Execute the current dummysubcmd command + command: dummysubcmd + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy4" + +- name: Get dummymain alternatives output + command: + cmd: '{{ alternatives_command }} --display dummymain' + register: result + +- name: Print result + debug: + var: result.stdout_lines + +- name: Switch to first alternative + alternatives: + name: dummymain + path: '/usr/bin/dummy1' + register: alternative + +- name: Check expected command was executed + assert: + that: + - 'alternative is changed' + +- name: Execute the current dummymain command + command: dummymain + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy1" + +- name: Execute the current dummysubcmd command + command: dummysubcmd + register: cmd + ignore_errors: true + +- name: Ensure that the subcommand is gone + assert: + that: + - cmd.rc == 2 + - '"No such file" in cmd.msg' + +- name: Get dummymain alternatives output + command: + cmd: '{{ alternatives_command }} --display dummymain' + register: result + +- name: Print result + debug: + var: result.stdout_lines + +- name: Switch to second alternative + alternatives: + name: dummymain + path: '/usr/bin/dummy3' + register: alternative + +- name: Check expected command was executed + assert: + that: + - 'alternative is changed' + +- name: Execute the current dummymain command + command: dummymain + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy3" + +- name: Execute the current dummysubcmd command + command: dummysubcmd + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy4" + +- name: Get dummymain alternatives output + command: + cmd: '{{ alternatives_command }} --display dummymain' + register: result + +- name: Print result + debug: + var: result.stdout_lines diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/test.yml new file mode 100644 index 000000000..ca59a4b55 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/test.yml @@ -0,0 +1,56 @@ +--- +# 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: ' with_alternatives: {{ with_alternatives }}, mode: {{ mode }}' + +- block: + - name: set alternative (using link parameter) + alternatives: + name: dummy + path: '/usr/bin/dummy{{ item }}' + link: '/usr/bin/dummy' + register: alternative + + - name: check expected command was executed + assert: + that: + - 'alternative is successful' + - 'alternative is changed' + when: with_link + +- block: + - name: set alternative (without link parameter) + alternatives: + name: dummy + path: '/usr/bin/dummy{{ item }}' + register: alternative + + - name: check expected command was executed + assert: + that: + - 'alternative is successful' + - 'alternative is changed' + when: not with_link + +- name: execute dummy command + shell: dummy + register: cmd + +- name: check expected command was executed + assert: + that: + - 'cmd.stdout == "dummy" ~ item' + +- name: 'check mode (manual: alternatives file existed, it has been updated)' + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"' + when: ansible_os_family != 'RedHat' or with_alternatives or item != 1 + +- name: 'check mode (auto: alternatives file didn''t exist, it has been created)' + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"' + when: ansible_os_family == 'RedHat' and not with_alternatives and item == 1 + +- name: check that alternative has been updated + command: "grep -Pzq '/bin/dummy{{ item }}\\n' '{{ alternatives_dir }}/dummy'" diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests.yml new file mode 100644 index 000000000..75e30cabe --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests.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 + +- block: + - include_tasks: remove_links.yml + - include_tasks: setup_test.yml + # at least two iterations: + # - first will use 'link currently absent', + # - second will receive 'link currently points to' + - include_tasks: test.yml + with_sequence: start=1 end=2 + vars: + with_link: '{{ test_conf[0] }}' + with_alternatives: '{{ test_conf[1] }}' + mode: '{{ test_conf[2] }}' + # update-alternatives included in Fedora 26 (1.10) & Red Hat 7.4 (1.8) doesn't provide + # '--query' switch, 'link' is mandatory for these distributions. + when: ansible_os_family != 'RedHat' or test_conf[0] 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 new file mode 100644 index 000000000..46cf48e59 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml @@ -0,0 +1,54 @@ +--- +# 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: update dummy alternative + alternatives: + name: dummy + path: '/usr/bin/dummy{{ item }}' + link: /usr/bin/dummy + priority: '{{ 60 + item|int }}' + register: alternative + +- name: execute dummy command + shell: dummy + register: cmd + +- name: check if link group is in manual mode + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"' + +- name: check expected command was executed + assert: + that: + - 'alternative is changed' + - 'cmd.stdout == "dummy{{ item }}"' + +- name: check that alternative has been updated + command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 60 + item|int }}' '{{ alternatives_dir }}/dummy'" + +- name: update dummy priority + alternatives: + name: dummy + path: '/usr/bin/dummy{{ item }}' + link: /usr/bin/dummy + priority: '{{ 70 + item|int }}' + register: alternative + +- name: check that alternative priority has been updated + command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 70 + item|int }}' '{{ alternatives_dir }}/dummy'" + +- name: no change without priority + alternatives: + name: dummy + path: '/usr/bin/dummy{{ item }}' + link: /usr/bin/dummy + register: alternative + +- name: check no change was triggered without priority + assert: + that: + - 'alternative is not changed' + +- name: check that alternative priority has not been changed + command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 70 + item|int }}' '{{ alternatives_dir }}/dummy'" diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_state.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_state.yml new file mode 100644 index 000000000..92c8078c2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_state.yml @@ -0,0 +1,120 @@ +--- +# 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 + +# Add a few dummy alternatives with state = present and make sure that the +# group is in 'auto' mode and the highest priority alternative is selected. +- name: Add some dummy alternatives with state = present + alternatives: + name: dummy + path: "/usr/bin/dummy{{ item.n }}" + link: /usr/bin/dummy + priority: "{{ item.priority }}" + state: present + loop: + - { n: 1, priority: 50 } + - { n: 2, priority: 70 } + - { n: 3, priority: 25 } + +- name: Ensure that the link group is in auto mode + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"' + +# Execute current selected 'dummy' and ensure it's the alternative we expect +- name: Execute the current dummy command + shell: dummy + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy2" + +# Add another alternative with state = 'selected' and make sure that +# this change results in the group being set to manual mode, and the +# new alternative being the selected one. +- name: Add another dummy alternative with state = selected + alternatives: + name: dummy + path: /usr/bin/dummy4 + link: /usr/bin/dummy + priority: 10 + state: selected + +- name: Ensure that the link group is in manual mode + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"' + +- name: Execute the current dummy command + shell: dummy + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy4" + +# Set the currently selected alternative to state = 'present' (was previously +# selected), and ensure that this results in the group not being set to 'auto' +# mode, and the alternative is still selected. +- name: Set current selected dummy to state = present + alternatives: + name: dummy + path: /usr/bin/dummy4 + link: /usr/bin/dummy + state: present + +- name: Ensure that the link group is in auto mode + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"' + +- name: Execute the current dummy command + shell: dummy + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy4" + +# Set the currently selected alternative to state = 'auto' (was previously +# selected), and ensure that this results in the group being set to 'auto' +# mode, and the highest priority alternative is selected. +- name: Set current selected dummy to state = present + alternatives: + name: dummy + path: /usr/bin/dummy4 + link: /usr/bin/dummy + state: auto + +- name: Ensure that the link group is in auto mode + shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"' + +- name: Execute the current dummy command + shell: dummy + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy2" + +# Remove an alternative with state = 'absent' and make sure that +# this change results in the alternative being removed. +- name: Remove best dummy alternative with state = absent + alternatives: + name: dummy + path: /usr/bin/dummy2 + state: absent + +- name: Ensure that the link group is in auto mode + shell: 'grep "/usr/bin/dummy2" {{ alternatives_dir }}/dummy' + register: cmd + failed_when: cmd.rc == 0 + +- name: Execute the current dummy command + shell: dummy + register: cmd + +- name: Ensure that the expected command was executed + assert: + that: + - cmd.stdout == "dummy1" diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_alternative b/ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_alternative new file mode 100644 index 000000000..9b7136d56 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_alternative @@ -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 +#} +{{ mode }} +/usr/bin/dummy + +{% if with_alternatives %} +/usr/bin/dummy1 +40 +/usr/bin/dummy2 +30 + +{% else %} + +{% endif %} diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_command b/ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_command new file mode 100644 index 000000000..afd80e673 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/templates/dummy_command @@ -0,0 +1,6 @@ +#!/bin/sh +# 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 + +echo dummy{{ item }} diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/vars/Debian.yml new file mode 100644 index 000000000..e7f87c59d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/vars/Debian.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 + +alternatives_dir: /var/lib/dpkg/alternatives/ +alternatives_command: update-alternatives diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/vars/Suse-42.3.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/vars/Suse-42.3.yml new file mode 100644 index 000000000..0d5a9cfec --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/vars/Suse-42.3.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 + +alternatives_dir: /var/lib/rpm/alternatives/ +alternatives_command: update-alternatives diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/vars/default.yml new file mode 100644 index 000000000..68e1feafa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/alternatives/vars/default.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 + +alternatives_dir: /var/lib/alternatives/ +alternatives_command: update-alternatives 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 new file mode 100644 index 000000000..13655b194 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/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/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/ansible_galaxy_install/files/test.yml b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/files/test.yml new file mode 100644 index 000000000..877b5fca4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/files/test.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 + +roles: + # Install a role from Ansible Galaxy. + - name: geerlingguy.java + version: 1.9.6 + +collections: + # Install a collection from Ansible Galaxy. + - name: geerlingguy.php_roles + version: 0.9.3 + source: https://galaxy.ansible.com diff --git a/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/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/ansible_galaxy_install/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml new file mode 100644 index 000000000..1ecd9980d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/tasks/main.yml @@ -0,0 +1,88 @@ +--- +# 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 collection netbox.netbox + community.general.ansible_galaxy_install: + type: collection + name: netbox.netbox + register: install_c0 + +- name: Assert collection netbox.netbox was installed + assert: + that: + - install_c0 is changed + - '"netbox.netbox" in install_c0.new_collections' + +- name: Install collection netbox.netbox (again) + community.general.ansible_galaxy_install: + type: collection + name: netbox.netbox + register: install_c1 + +- name: Assert collection was not installed + assert: + that: + - install_c1 is not changed + +################################################### +- name: Install role ansistrano.deploy + community.general.ansible_galaxy_install: + type: role + name: ansistrano.deploy + register: install_r0 + +- name: Assert collection ansistrano.deploy was installed + assert: + that: + - install_r0 is changed + - '"ansistrano.deploy" in install_r0.new_roles' + +- name: Install role ansistrano.deploy (again) + community.general.ansible_galaxy_install: + type: role + name: ansistrano.deploy + register: install_r1 + +- name: Assert role was not installed + assert: + that: + - install_r1 is not changed + +################################################### +- name: Set requirements file path + set_fact: + reqs_file: '{{ remote_tmp_dir }}/reqs.yaml' + +- name: Copy requirements file + copy: + src: 'files/test.yml' + dest: '{{ reqs_file }}' + +- name: Install from requirements file + community.general.ansible_galaxy_install: + type: both + requirements_file: "{{ reqs_file }}" + register: install_rq0 + ignore_errors: true + +- name: Assert requirements file was installed + assert: + that: + - install_rq0 is changed + - '"geerlingguy.java" in install_rq0.new_roles' + - '"geerlingguy.php_roles" in install_rq0.new_collections' + +- name: Install from requirements file (again) + community.general.ansible_galaxy_install: + type: both + requirements_file: "{{ reqs_file }}" + register: install_rq1 + ignore_errors: true + +- name: Assert requirements file was not installed + assert: + that: + - install_rq1 is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/apache2_module/aliases b/ansible_collections/community/general/tests/integration/targets/apache2_module/aliases new file mode 100644 index 000000000..0d1324b22 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/apache2_module/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 +destructive +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/635-apache2-misleading-warning.yml b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/635-apache2-misleading-warning.yml new file mode 100644 index 000000000..5d93a9d30 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/635-apache2-misleading-warning.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 +# This test represent the misleading behavior of the following issue: https://github.com/ansible-collections/community.general/issues/635 +- name: Disable MPM event module + apache2_module: + name: "{{ item.module}}" + state: "{{ item.state}}" + ignore_configcheck: true + register: disable_mpm_modules + with_items: + - { module: mpm_event, state: absent } + - { module: mpm_prefork, state: present } + +- assert: + that: + - "'warnings' in disable_mpm_modules" + - disable_mpm_modules["warnings"] == [ + "No MPM module loaded! apache2 reload AND other module actions will fail if no MPM module is loaded immediately.", + "No MPM module loaded! apache2 reload AND other module actions will fail if no MPM module is loaded immediately." + ] + +- name: Enable MPM event module - Revert previous change + apache2_module: + name: "{{ item.module}}" + state: "{{ item.state}}" + ignore_configcheck: true + register: disable_mpm_modules + with_items: + - { module: mpm_prefork, state: absent } + - { module: mpm_event, state: present } + +- name: Disable MPM event module + apache2_module: + name: "{{ item.module}}" + state: "{{ item.state}}" + ignore_configcheck: true + warn_mpm_absent: false + register: disable_mpm_modules + with_items: + - { module: mpm_event, state: absent } + - { module: mpm_prefork, state: present } + +- assert: + that: + - "'warnings' not in disable_mpm_modules" 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 new file mode 100644 index 000000000..3301a16b1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml @@ -0,0 +1,207 @@ +--- +# 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: disable userdir module + community.general.apache2_module: + name: userdir + state: absent + register: userdir_first_disable + +- name: disable userdir module, second run + community.general.apache2_module: + name: userdir + state: absent + register: disable + +- name: ensure community.general.apache2_module is idempotent + assert: + that: + - disable is not changed + +- name: enable userdir module + community.general.apache2_module: + name: userdir + state: present + register: enable + +- name: ensure changed on successful enable + assert: + that: + - enable is changed + +- name: enable userdir module, second run + community.general.apache2_module: + name: userdir + state: present + register: enabletwo + +- name: ensure community.general.apache2_module is idempotent + assert: + that: + - 'not enabletwo.changed' + +- name: disable userdir module, final run + community.general.apache2_module: + name: userdir + state: absent + register: disablefinal + +- name: ensure changed on successful disable + assert: + that: + - 'disablefinal.changed' + +- name: set userdir to original state + community.general.apache2_module: + name: userdir + state: present + when: userdir_first_disable is changed + +- name: ensure autoindex enabled + community.general.apache2_module: + name: autoindex + state: present + +- name: Debian/Ubuntu specific tests + when: "ansible_os_family == 'Debian'" + block: + - name: force disable of autoindex # bug #2499 + community.general.apache2_module: + name: autoindex + state: absent + force: true + + - name: reenable autoindex + community.general.apache2_module: + name: autoindex + state: present + + # mod_evasive is enabled by default upon the installation, so disable first and enable second, to preserve the config + - name: disable evasive module + community.general.apache2_module: + name: evasive + state: absent + + - name: enable evasive module, test https://github.com/ansible/ansible/issues/22635 + community.general.apache2_module: + name: evasive + state: present + + - name: use identifier to enable module, fix for https://github.com/ansible/ansible/issues/33669 + community.general.apache2_module: + name: dump_io + state: present + ignore_errors: true + register: enable_dumpio_wrong + + - name: disable dump_io + community.general.apache2_module: + name: dump_io + identifier: dumpio_module + state: absent + + - name: use identifier to enable module, fix for https://github.com/ansible/ansible/issues/33669 + community.general.apache2_module: + name: dump_io + identifier: dumpio_module + state: present + register: enable_dumpio_correct_1 + + - name: ensure idempotency with identifier + community.general.apache2_module: + name: dump_io + identifier: dumpio_module + state: present + register: enable_dumpio_correct_2 + + - name: disable dump_io + community.general.apache2_module: + name: dump_io + identifier: dumpio_module + state: absent + + - assert: + that: + - enable_dumpio_wrong is failed + - enable_dumpio_correct_1 is changed + - enable_dumpio_correct_2 is not changed + + - name: disable mpm modules + community.general.apache2_module: + name: "{{ item }}" + state: absent + ignore_configcheck: true + with_items: + - mpm_worker + - mpm_event + - mpm_prefork + + - name: enabled mpm_event + community.general.apache2_module: + name: mpm_event + state: present + ignore_configcheck: true + register: enabledmpmevent + + - name: ensure changed mpm_event + assert: + that: + - 'enabledmpmevent.changed' + + - name: switch between mpm_event and mpm_worker + community.general.apache2_module: + name: "{{ item.name }}" + state: "{{ item.state }}" + ignore_configcheck: true + with_items: + - name: mpm_event + state: absent + - name: mpm_worker + state: present + + - name: ensure mpm_worker is already enabled + community.general.apache2_module: + name: mpm_worker + state: present + register: enabledmpmworker + + - name: ensure mpm_worker unchanged + assert: + that: + - 'not enabledmpmworker.changed' + + - name: try to disable all mpm modules with configcheck + community.general.apache2_module: + name: "{{item}}" + state: absent + with_items: + - mpm_worker + - mpm_event + - mpm_prefork + ignore_errors: true + register: remove_with_configcheck + + - name: ensure configcheck fails task with when run without mpm modules + assert: + that: + - "{{ item.failed }}" + with_items: "{{ remove_with_configcheck.results }}" + + - name: try to disable all mpm modules without configcheck + community.general.apache2_module: + name: "{{item}}" + state: absent + ignore_configcheck: true + with_items: + - mpm_worker + - mpm_event + - mpm_prefork + + - name: enabled mpm_event to restore previous state + community.general.apache2_module: + name: mpm_event + state: present + ignore_configcheck: true + register: enabledmpmevent diff --git a/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/main.yml new file mode 100644 index 000000000..6f2f718ad --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/main.yml @@ -0,0 +1,52 @@ +--- +#################################################################### +# 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 apache via apt + apt: + name: "{{item}}" + state: present + when: "ansible_os_family == 'Debian'" + with_items: + - apache2 + - libapache2-mod-evasive + +- name: install apache via zypper + community.general.zypper: + name: apache2 + state: present + when: "ansible_os_family == 'Suse'" + +- name: test apache2_module + block: + - name: get list of enabled modules + shell: apache2ctl -M | sort + register: modules_before + - name: include only on supported systems + include_tasks: actualtest.yml + always: + - name: get list of enabled modules + shell: apache2ctl -M | sort + register: modules_after + - name: modules_before + debug: + var: modules_before + - name: modules_after + debug: + var: modules_after + - name: ensure that all test modules are disabled again + assert: + that: modules_before.stdout == modules_after.stdout + when: ansible_os_family in ['Debian', 'Suse'] + # centos/RHEL does not have a2enmod/a2dismod + +- name: include misleading warning test + include_tasks: 635-apache2-misleading-warning.yml + when: ansible_os_family in ['Debian'] + # Suse has mpm_event module compiled within the base apache2 \ No newline at end of file diff --git a/ansible_collections/community/general/tests/integration/targets/archive/aliases b/ansible_collections/community/general/tests/integration/targets/archive/aliases new file mode 100644 index 000000000..88b15a24f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/aliases @@ -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 + +azp/posix/2 +needs/root +destructive +skip/aix +skip/osx # FIXME diff --git a/ansible_collections/community/general/tests/integration/targets/archive/files/bar.txt b/ansible_collections/community/general/tests/integration/targets/archive/files/bar.txt new file mode 100644 index 000000000..32276adb8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/files/bar.txt @@ -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 + +bar.txt diff --git a/ansible_collections/community/general/tests/integration/targets/archive/files/empty.txt b/ansible_collections/community/general/tests/integration/targets/archive/files/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/integration/targets/archive/files/foo.txt b/ansible_collections/community/general/tests/integration/targets/archive/files/foo.txt new file mode 100644 index 000000000..a40d2f008 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/files/foo.txt @@ -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 + +foo.txt diff --git a/ansible_collections/community/general/tests/integration/targets/archive/files/sub/subfile.txt b/ansible_collections/community/general/tests/integration/targets/archive/files/sub/subfile.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/integration/targets/archive/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/archive/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/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/archive/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/archive/tasks/main.yml new file mode 100644 index 000000000..4ca41e254 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/tasks/main.yml @@ -0,0 +1,145 @@ +--- +#################################################################### +# 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 archive module. +# Copyright (c) 2017, Abhijeet Kasurde +# 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 + +# Make sure we start fresh + +# Test setup +- name: prep our files + copy: src={{ item }} dest={{remote_tmp_dir}}/{{ item }} + with_items: + - foo.txt + - bar.txt + - empty.txt + - sub + - sub/subfile.txt + +# Run twice without lzma backport installed, to make sure it does not crash +- name: Archive - pre-test - first run + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_pretest_1.tar" + format: "tar" + register: pretest_1 + +- name: Archive - pre-test - second run + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_pretest_1.tar" + format: "tar" + register: pretest_2 + +- name: Archive - validate pre-test + assert: + that: + - pretest_1 is changed + - pretest_2 is not changed + +# Install dependencies +- name: Ensure zip is present to create test archive (yum) + yum: name=zip state=latest + when: ansible_facts.pkg_mgr == 'yum' + +- name: Ensure zip is present to create test archive (apt) + apt: name=zip state=latest + when: ansible_facts.pkg_mgr == 'apt' + +- name: Install prerequisites for backports.lzma when using python2 (non OSX) + block: + - name: Set liblzma package name depending on the OS + set_fact: + liblzma_dev_package: + Debian: liblzma-dev + RedHat: xz-devel + Suse: xz-devel + - name: Ensure liblzma-dev is present to install backports-lzma + package: name={{ liblzma_dev_package[ansible_os_family] }} state=latest + when: ansible_os_family in liblzma_dev_package.keys() + when: + - ansible_python_version.split('.')[0] == '2' + - ansible_os_family != 'Darwin' + +- name: Install prerequisites for backports.lzma when using python2 (OSX) + block: + - name: Find brew binary + command: which brew + register: brew_which + - name: Get owner of brew binary + stat: path="{{ brew_which.stdout }}" + register: brew_stat + - name: "Install package" + homebrew: + name: xz + state: present + update_homebrew: false + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + # Newer versions of brew want to compile a package which takes a long time. Do not upgrade homebrew until a + # proper solution can be found + environment: + HOMEBREW_NO_AUTO_UPDATE: "True" + when: + - ansible_python_version.split('.')[0] == '2' + - ansible_os_family == 'Darwin' + +- name: Ensure backports.lzma is present to create test archive (pip) + pip: name=backports.lzma state=latest + when: ansible_python_version.split('.')[0] == '2' + register: backports_lzma_pip + +- name: Define formats to test + set_fact: + formats: + - tar + - zip + - gz + - bz2 + - xz + +# Run tests +- name: Run core tests + include_tasks: + file: ../tests/core.yml + loop: "{{ formats }}" + loop_control: + loop_var: format + +- name: Run exclusions tests + include_tasks: + file: ../tests/exclusions.yml + loop: "{{ formats }}" + loop_control: + loop_var: format + +- name: Run remove tests + include_tasks: + file: ../tests/remove.yml + loop: "{{ formats }}" + loop_control: + loop_var: format + +- name: Run broken link tests + include_tasks: + file: ../tests/broken-link.yml + loop: "{{ formats }}" + loop_control: + loop_var: format + +- name: Run Idempotency tests + include_tasks: + file: ../tests/idempotency.yml + loop: "{{ formats }}" + loop_control: + loop_var: format + +# Test cleanup +- name: Remove backports.lzma if previously installed (pip) + pip: name=backports.lzma state=absent + when: backports_lzma_pip is changed diff --git a/ansible_collections/community/general/tests/integration/targets/archive/tests/broken-link.yml b/ansible_collections/community/general/tests/integration/targets/archive/tests/broken-link.yml new file mode 100644 index 000000000..7c6444371 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/broken-link.yml @@ -0,0 +1,35 @@ +--- +# 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 link - broken link ({{ format }}) + file: + src: /nowhere + dest: "{{ remote_tmp_dir }}/nowhere.txt" + state: link + force: true + + - name: Archive - broken link ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_broken_link.{{ format }}" + format: "{{ format }}" + + - name: Verify archive exists - broken link ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_broken_link.{{ format }}" + state: file + + - name: Remove archive - broken link ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_broken_link.{{ format }}" + state: absent + + - name: Remove link - broken link ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/nowhere.txt" + state: absent + # 'zip' does not support symlink's + when: format != 'zip' 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 new file mode 100644 index 000000000..1c4f4d1aa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml @@ -0,0 +1,177 @@ +--- +#################################################################### +# 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 archive module. +# Copyright (c) 2017, Abhijeet Kasurde +# 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 + +# Make sure we start fresh + +# Core functionality tests +- name: Archive - no options ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_no_opts.{{ format }}" + format: "{{ format }}" + register: archive_no_options + +- name: Verify that archive exists - no options ({{ format }}) + file: + path: "{{remote_tmp_dir}}/archive_no_opts.{{ format }}" + state: file + +- name: Verify that archive result is changed and includes all files - no options ({{ format }}) + assert: + that: + - archive_no_options is changed + - "archive_no_options.dest_state == 'archive'" + - "{{ archive_no_options.archived | length }} == 3" + +- name: Remove the archive - no options ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_no_options.{{ format }}" + state: absent + +- name: Archive - file options ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_file_options.{{ format }}" + format: "{{ format }}" + mode: "u+rwX,g-rwx,o-rwx" + register: archive_file_options + +- name: Retrieve archive file information - file options ({{ format }}) + stat: + path: "{{ remote_tmp_dir }}/archive_file_options.{{ format }}" + register: archive_file_options_stat + +- name: Test that the file modes were changed + assert: + that: + - archive_file_options_stat is not changed + - "archive_file_options.mode == '0600'" + - "{{ archive_file_options.archived | length }} == 3" + +- name: Remove the archive - file options ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_file_options.{{ format }}" + state: absent + +- name: Archive - non-ascii ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_nonascii_くらとみ.{{ format }}" + format: "{{ format }}" + register: archive_nonascii + +- name: Retrieve archive file information - non-ascii ({{ format }}) + stat: + path: "{{ remote_tmp_dir }}/archive_nonascii_くらとみ.{{ format }}" + register: archive_nonascii_stat + +- name: Test that archive exists - non-ascii ({{ format }}) + assert: + that: + - archive_nonascii is changed + - archive_nonascii_stat.stat.exists == true + +- name: Remove the archive - non-ascii ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_nonascii_くらとみ.{{ format }}" + state: absent + +- name: Archive - single target ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/foo.txt" + dest: "{{ remote_tmp_dir }}/archive_single_target.{{ format }}" + format: "{{ format }}" + register: archive_single_target + +- name: Assert archive has correct state - single target ({{ format }}) + assert: + that: + - archive_single_target.dest_state == state_map[format] + vars: + state_map: + tar: archive + zip: archive + gz: compress + bz2: compress + xz: compress + +- block: + - name: Retrieve contents of archive - single target ({{ format }}) + ansible.builtin.unarchive: + src: "{{ remote_tmp_dir }}/archive_single_target.{{ format }}" + dest: . + list_files: true + check_mode: true + ignore_errors: true + register: archive_single_target_contents + + - name: Assert that file names are preserved - single target ({{ format }}) + assert: + that: + - "'oo.txt' not in archive_single_target_contents.files" + - "'foo.txt' in archive_single_target_contents.files" + # ``unarchive`` fails for RHEL and FreeBSD on ansible 2.x + when: archive_single_target_contents is success and archive_single_target_contents is not skipped + when: "format == 'zip'" + +- name: Remove archive - single target ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_single_target.{{ format }}" + state: absent + +- name: Archive - path list ({{ format }}) + archive: + path: + - "{{ remote_tmp_dir }}/empty.txt" + - "{{ remote_tmp_dir }}/foo.txt" + - "{{ remote_tmp_dir }}/bar.txt" + dest: "{{ remote_tmp_dir }}/archive_path_list.{{ format }}" + format: "{{ format }}" + register: archive_path_list + +- name: Verify that archive exists - path list ({{ format }}) + file: + path: "{{remote_tmp_dir}}/archive_path_list.{{ format }}" + state: file + +- name: Assert that archive contains all files - path list ({{ format }}) + assert: + that: + - archive_path_list is changed + - "{{ archive_path_list.archived | length }} == 3" + +- name: Remove archive - path list ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_path_list.{{ format }}" + state: absent + +- name: Archive - missing paths ({{ format }}) + archive: + path: + - "{{ remote_tmp_dir }}/*.txt" + - "{{ remote_tmp_dir }}/dne.txt" + exclude_path: "{{ remote_tmp_dir }}/foo.txt" + dest: "{{ remote_tmp_dir }}/archive_missing_paths.{{ format }}" + format: "{{ format }}" + register: archive_missing_paths + +- name: Assert that incomplete archive has incomplete state - missing paths ({{ format }}) + assert: + 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" + +- name: Remove archive - missing paths ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_missing_paths.{{ format }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/archive/tests/exclusions.yml b/ansible_collections/community/general/tests/integration/targets/archive/tests/exclusions.yml new file mode 100644 index 000000000..3c5f1fc5c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/exclusions.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: Archive - exclusion patterns ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_exclusion_patterns.{{ format }}" + format: "{{ format }}" + exclusion_patterns: b?r.* + register: archive_exclusion_patterns + +- name: Assert that only included files are archived - exclusion patterns ({{ format }}) + assert: + that: + - archive_exclusion_patterns is changed + - "'bar.txt' not in archive_exclusion_patterns.archived" + +- name: Remove archive - exclusion patterns ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_exclusion_patterns.{{ format }}" + state: absent + +- name: Archive - exclude path ({{ format }}) + archive: + path: + - "{{ remote_tmp_dir }}/sub/subfile.txt" + - "{{ remote_tmp_dir }}" + exclude_path: + - "{{ remote_tmp_dir }}" + dest: "{{ remote_tmp_dir }}/archive_exclude_paths.{{ format }}" + format: "{{ format }}" + register: archive_excluded_paths + +- name: Assert that excluded paths do not influence archive root - exclude path ({{ format }}) + assert: + that: + - archive_excluded_paths.arcroot != remote_tmp_dir + +- name: Remove archive - exclude path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_exclude_paths.{{ format }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/archive/tests/idempotency.yml b/ansible_collections/community/general/tests/integration/targets/archive/tests/idempotency.yml new file mode 100644 index 000000000..32f20a656 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/idempotency.yml @@ -0,0 +1,144 @@ +--- +# 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: Archive - file content idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_file_content_idempotency.{{ format }}" + format: "{{ format }}" + register: file_content_idempotency_before + +- name: Modify file - file content idempotency ({{ format }}) + lineinfile: + line: bar.txt + regexp: "^foo.txt$" + path: "{{ remote_tmp_dir }}/foo.txt" + +- name: Archive second time - file content idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_file_content_idempotency.{{ format }}" + format: "{{ format }}" + register: file_content_idempotency_after + +- name: Assert task status is changed - file content idempotency ({{ format }}) + assert: + that: + - file_content_idempotency_after is changed + # Only ``zip`` archives are guaranteed to compare file content checksums rather than header checksums + when: "format == 'zip'" + +- name: Remove archive - file content idempotency ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_file_content_idempotency.{{ format }}" + state: absent + +- name: Modify file back - file content idempotency ({{ format }}) + lineinfile: + line: foo.txt + regexp: "^bar.txt$" + path: "{{ remote_tmp_dir }}/foo.txt" + +- name: Archive - file name idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_file_name_idempotency.{{ format }}" + format: "{{ format }}" + register: file_name_idempotency_before + +- name: Rename file - file name idempotency ({{ format }}) + command: "mv {{ remote_tmp_dir }}/foo.txt {{ remote_tmp_dir }}/fii.txt" + +- name: Archive again - file name idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_file_name_idempotency.{{ format }}" + format: "{{ format }}" + register: file_name_idempotency_after + +- name: Check task status - file name idempotency ({{ format }}) + assert: + that: + - file_name_idempotency_after is changed + +- name: Remove archive - file name idempotency ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_file_name_idempotency.{{ format }}" + state: absent + +- name: Rename file back - file name idempotency ({{ format }}) + command: "mv {{ remote_tmp_dir }}/fii.txt {{ remote_tmp_dir }}/foo.txt" + +- name: Archive - single file content idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/foo.txt" + dest: "{{ remote_tmp_dir }}/archive_single_file_content_idempotency.{{ format }}" + format: "{{ format }}" + register: single_file_content_idempotency_before + +- name: Modify file - single file content idempotency ({{ format }}) + lineinfile: + line: bar.txt + regexp: "^foo.txt$" + path: "{{ remote_tmp_dir }}/foo.txt" + +- name: Archive second time - single file content idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/foo.txt" + dest: "{{ remote_tmp_dir }}/archive_single_file_content_idempotency.{{ format }}" + format: "{{ format }}" + register: single_file_content_idempotency_after + +- name: Assert task status is changed - single file content idempotency ({{ format }}) + assert: + that: + - single_file_content_idempotency_after is changed + # ``tar`` archives are not guaranteed to identify changes to file content if the file meta properties are unchanged. + when: "format != 'tar'" + +- name: Remove archive - single file content idempotency ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_single_file_content_idempotency.{{ format }}" + state: absent + +- name: Modify file back - single file content idempotency ({{ format }}) + lineinfile: + line: foo.txt + regexp: "^bar.txt$" + path: "{{ remote_tmp_dir }}/foo.txt" + +- name: Archive - single file name idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/foo.txt" + dest: "{{ remote_tmp_dir }}/archive_single_file_name_idempotency.{{ format }}" + format: "{{ format }}" + register: single_file_name_idempotency_before + +- name: Rename file - single file name idempotency ({{ format }}) + command: "mv {{ remote_tmp_dir }}/foo.txt {{ remote_tmp_dir }}/fii.txt" + +- name: Archive again - single file name idempotency ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/fii.txt" + dest: "{{ remote_tmp_dir }}/archive_single_file_name_idempotency.{{ format }}" + format: "{{ format }}" + register: single_file_name_idempotency_after + + +# The gz, bz2, and xz formats do not store the original file name +# so it is not possible to identify a change in this scenario. +- name: Check task status - single file name idempotency ({{ format }}) + assert: + that: + - single_file_name_idempotency_after is changed + when: "format in ('tar', 'zip')" + +- name: Remove archive - single file name idempotency ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_single_file_name_idempotency.{{ format }}" + state: absent + +- name: Rename file back - single file name idempotency ({{ format }}) + command: "mv {{ remote_tmp_dir }}/fii.txt {{ remote_tmp_dir }}/foo.txt" 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 new file mode 100644 index 000000000..8f0b8cff8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml @@ -0,0 +1,211 @@ +--- +# 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: Archive - remove source files ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/*.txt" + dest: "{{ remote_tmp_dir }}/archive_remove_source_files.{{ format }}" + format: "{{ format }}" + remove: true + register: archive_remove_source_files + +- name: Verify archive exists - remove source files ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_files.{{ format }}" + state: file + +- name: Verify all files were archived - remove source files ({{ format }}) + assert: + that: + - archive_remove_source_files is changed + - "{{ 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" + with_items: + - foo.txt + - bar.txt + - empty.txt + +- name: Copy source files - remove source directory ({{ format }}) + copy: + src: "{{ item }}" + dest: "{{ remote_tmp_dir }}/{{ item }}" + with_items: + - foo.txt + - bar.txt + - empty.txt + +- name: Create temporary directory - remove source directory ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/tmpdir" + state: directory + +- name: Copy source files to temporary directory - remove source directory ({{ format }}) + copy: + src: "{{ item }}" + dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}" + with_items: + - foo.txt + - bar.txt + - empty.txt + +- name: Archive - remove source directory ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/tmpdir" + dest: "{{ remote_tmp_dir }}/archive_remove_source_directory.{{ format }}" + format: "{{ format }}" + remove: true + register: archive_remove_source_directory + +- name: Verify archive exists - remove source directory ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_directory.{{ format }}" + state: file + +- name: Verify archive contains all files - remove source directory ({{ format }}) + assert: + that: + - archive_remove_source_directory is changed + - "{{ 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: Verify source directory was removed - remove source directory ({{ format }}) + assert: + that: + - "'{{ remote_tmp_dir }}/tmpdir' is not exists" + +- name: Create temporary directory - remove source excluding path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/tmpdir" + state: directory + +- name: Copy source files to temporary directory - remove source excluding path ({{ format }}) + copy: + src: "{{ item }}" + dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}" + with_items: + - foo.txt + - bar.txt + - empty.txt + +- name: Archive - remove source excluding path ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/tmpdir/*" + dest: "{{ remote_tmp_dir }}/archive_remove_source_excluding_path.{{ format }}" + format: "{{ format }}" + remove: true + exclude_path: "{{ remote_tmp_dir }}/tmpdir/empty.txt" + register: archive_remove_source_excluding_path + +- name: Verify archive exists - remove source excluding path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_path.{{ format }}" + state: file + +- name: Verify all files except excluded are archived - remove source excluding path ({{ format }}) + assert: + that: + - archive_remove_source_excluding_path is changed + - "{{ archive_remove_source_excluding_path.archived | length }} == 2" + +- name: Remove archive - remove source excluding path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_path.{{ format }}" + state: absent + +- name: Verify that excluded file still exists - remove source excluding path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/tmpdir/empty.txt" + state: file + +- name: Copy source files to temporary directory - remove source excluding sub path ({{ format }}) + copy: + src: "{{ item }}" + dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}" + with_items: + - foo.txt + - bar.txt + - empty.txt + - sub + - sub/subfile.txt + +- name: Archive - remove source excluding sub path ({{ format }}) + archive: + path: + - "{{ remote_tmp_dir }}/tmpdir/*.txt" + - "{{ remote_tmp_dir }}/tmpdir/sub/*" + dest: "{{ remote_tmp_dir }}/archive_remove_source_excluding_sub_path.{{ format }}" + format: "{{ format }}" + remove: true + exclude_path: "{{ remote_tmp_dir }}/tmpdir/sub/subfile.txt" + register: archive_remove_source_excluding_sub_path + +- name: Verify archive exists - remove source excluding sub path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_sub_path.{{ format }}" + state: file + +- name: Remove archive - remove source excluding sub path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_sub_path.{{ format }}" + state: absent + +- name: Verify that sub path still exists - remove source excluding sub path ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/tmpdir/sub/subfile.txt" + state: file + +- name: Copy source files to temporary directory - remove source with nested paths ({{ format }}) + copy: + src: "{{ item }}" + dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}" + with_items: + - foo.txt + - bar.txt + - empty.txt + - sub + - sub/subfile.txt + +- name: Archive - remove source with nested paths ({{ format }}) + archive: + path: "{{ remote_tmp_dir }}/tmpdir/" + dest: "{{ remote_tmp_dir }}/archive_remove_source_nested_paths.{{ format }}" + format: "{{ format }}" + remove: true + register: archive_remove_nested_paths + +- name: Verify archive exists - remove source with nested paths ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_nested_paths.{{ format }}" + state: file + +- name: Verify source files were removed - remove source with nested paths ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/tmpdir" + state: absent + register: archive_remove_nested_paths_status + +- name: Assert tasks status - remove source with nested paths ({{ format }}) + assert: + that: + - archive_remove_nested_paths is success + - archive_remove_nested_paths_status is not changed + +- name: Remove archive - remove source with nested paths ({{ format }}) + file: + path: "{{ remote_tmp_dir }}/archive_remove_source_nested_paths.{{ format }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases new file mode 100644 index 000000000..914c36ad3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases @@ -0,0 +1,12 @@ +# Copyright (c) Ansible Projec +# 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 +azp/posix/vm +destructive +needs/privileged +skip/aix +skip/freebsd +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/defaults/main.yml new file mode 100644 index 000000000..52c88d5de --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/defaults/main.yml @@ -0,0 +1,20 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 + +btrfs_subvolume_single_configs: +- file: "/tmp/disks0.img" + loop: "/dev/loop95" +btrfs_subvolume_multiple_configs: +- file: "/tmp/diskm0.img" + loop: "/dev/loop97" +- file: "/tmp/diskm1.img" + loop: "/dev/loop98" +- file: "/tmp/diskm2.img" + loop: "/dev/loop99" +btrfs_subvolume_configs: "{{ btrfs_subvolume_single_configs + btrfs_subvolume_multiple_configs }}" +btrfs_subvolume_single_devices: "{{ btrfs_subvolume_single_configs | map(attribute='loop') }}" +btrfs_subvolume_single_label: "single" +btrfs_subvolume_multiple_devices: "{{ btrfs_subvolume_multiple_configs | map(attribute='loop') }}" +btrfs_subvolume_multiple_label: "multiple" diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/main.yml new file mode 100644 index 000000000..d47270440 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/main.yml @@ -0,0 +1,29 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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: Install required packages + ansible.builtin.package: + name: + - btrfs-progs # btrfs userspace + - util-linux # losetup + ignore_errors: True + register: btrfs_installed + +- name: Execute integration tests tests + block: + - ansible.builtin.include_tasks: 'setup.yml' + + - name: "Execute test scenario for single device filesystem" + ansible.builtin.include_tasks: 'run_filesystem_tests.yml' + vars: + btrfs_subvolume_target_device: "{{ btrfs_subvolume_single_devices | first }}" + btrfs_subvolume_target_label: "{{ btrfs_subvolume_single_label }}" + + - name: "Execute test scenario for multiple device configuration" + ansible.builtin.include_tasks: 'run_filesystem_tests.yml' + vars: + btrfs_subvolume_target_device: "{{ btrfs_subvolume_multiple_devices | first }}" + btrfs_subvolume_target_label: "{{ btrfs_subvolume_multiple_label }}" + when: btrfs_installed is success diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_common_tests.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_common_tests.yml new file mode 100644 index 000000000..013ec50bf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_common_tests.yml @@ -0,0 +1,15 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 + +- ansible.builtin.include_tasks: 'test_subvolume_simple.yml' +- ansible.builtin.include_tasks: 'test_subvolume_nested.yml' +- ansible.builtin.include_tasks: 'test_subvolume_recursive.yml' +- ansible.builtin.include_tasks: 'test_subvolume_default.yml' + +- ansible.builtin.include_tasks: 'test_snapshot_skip.yml' +- ansible.builtin.include_tasks: 'test_snapshot_clobber.yml' +- ansible.builtin.include_tasks: 'test_snapshot_error.yml' + +- ansible.builtin.include_tasks: 'test_subvolume_whitespace.yml' diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_filesystem_tests.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_filesystem_tests.yml new file mode 100644 index 000000000..0ea3fa666 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/run_filesystem_tests.yml @@ -0,0 +1,32 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 + +- ansible.builtin.include_tasks: 'test_filesystem_matching.yml' + +- name: "Execute all test scenario for unmounted filesystem" + ansible.builtin.include_tasks: 'run_common_tests.yml' + +- name: "Execute test scenarios where non-root subvolume is mounted" + block: + - name: Create subvolume '/nonroot' + community.general.btrfs_subvolume: + automount: Yes + name: "/nonroot" + filesystem_label: "{{ btrfs_subvolume_target_label }}" + state: "present" + register: nonroot + - name: "Mount subvolume '/nonroot'" + ansible.posix.mount: + src: "{{ nonroot.filesystem.devices | first }}" + path: /mnt + opts: "subvolid={{ nonroot.target_subvolume_id }}" + fstype: btrfs + state: mounted + - name: "Run tests for explicit, mounted single device configuration" + ansible.builtin.include_tasks: 'run_common_tests.yml' + - name: "Unmount subvolume /nonroot" + ansible.posix.mount: + path: /mnt + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/setup.yml new file mode 100644 index 000000000..f5bbdf9c5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/setup.yml @@ -0,0 +1,37 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 file {{ item.file }} to back loop device {{ item.loop }}" + ansible.builtin.command: + cmd: "dd if=/dev/zero of={{ item.file }} bs=1M count=200" ## minimum count 109 + creates: "{{ item.file }}" + with_items: "{{ btrfs_subvolume_configs }}" + +- name: "Setup loop device {{ item.loop }}" + ansible.builtin.command: + cmd: "losetup {{ item.loop }} {{ item.file }}" + creates: "{{ item.loop }}" + with_items: "{{ btrfs_subvolume_configs }}" + +- name: Create single device btrfs filesystem + ansible.builtin.command: + cmd: "mkfs.btrfs --label {{ btrfs_subvolume_single_label }} -f {{ btrfs_subvolume_single_devices | first }}" + changed_when: True + +- name: Create multiple device btrfs filesystem + ansible.builtin.command: + cmd: "mkfs.btrfs --label {{ btrfs_subvolume_multiple_label }} -f -d raid0 {{ btrfs_subvolume_multiple_devices | join(' ') }}" + changed_when: True + +# Typically created by udev, but apparently missing on Alpine +- name: Create btrfs control device node + ansible.builtin.command: + cmd: "mknod /dev/btrfs-control c 10 234" + creates: "/dev/btrfs-control" + +- name: Force rescan to ensure all device are detected + ansible.builtin.command: + cmd: "btrfs device scan" + changed_when: True diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_filesystem_matching.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_filesystem_matching.yml new file mode 100644 index 000000000..2455eeacf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_filesystem_matching.yml @@ -0,0 +1,80 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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: "Match targeted filesystem by label" + block: + - name: Match '{{ btrfs_subvolume_target_label }}' filesystem by label + community.general.btrfs_subvolume: + automount: Yes + name: "/match_label" + filesystem_label: "{{ btrfs_subvolume_target_label }}" + state: "present" + register: result + + - name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen + ansible.builtin.assert: + that: + - result.filesystem.label == btrfs_subvolume_target_label + +- name: "Match targeted filesystem by uuid" + block: + - name: Match '{{ btrfs_subvolume_target_label }}' filesystem by uuid + community.general.btrfs_subvolume: + automount: Yes + name: "/match_uuid" + filesystem_uuid: "{{ result.filesystem.uuid }}" + state: "present" + register: result + + - name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen + ansible.builtin.assert: + that: + - result.filesystem.label == btrfs_subvolume_target_label + +- name: "Match targeted filesystem by devices" + block: + - name: Match '{{ btrfs_subvolume_target_label }}' filesystem by device + community.general.btrfs_subvolume: + automount: Yes + name: "/match_device" + filesystem_device: "{{ result.filesystem.devices | first }}" + state: "present" + register: result + + - name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen + ansible.builtin.assert: + that: + - result.filesystem.label == btrfs_subvolume_target_label + +- name: "Match only mounted filesystem" + block: + - name: "Mount filesystem '{{ btrfs_subvolume_target_label }}'" + ansible.posix.mount: + src: "{{ result.filesystem.devices | first }}" + path: /mnt + opts: "subvolid={{ 5 }}" + fstype: btrfs + state: mounted + + - name: Print current status + community.general.btrfs_info: + + - name: Match '{{ btrfs_subvolume_target_label }}' filesystem when only mount + community.general.btrfs_subvolume: + automount: Yes + name: "/match_only_mounted" + state: "present" + register: result + + - name: "Unmount filesystem '{{ btrfs_subvolume_target_label }}'" + ansible.posix.mount: + path: /mnt + state: absent + + - name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen + ansible.builtin.assert: + that: + - result.filesystem.label == btrfs_subvolume_target_label + when: False # TODO don't attempt this if the host already has a pre-existing btrfs filesystem diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_clobber.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_clobber.yml new file mode 100644 index 000000000..ce25a999b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_clobber.yml @@ -0,0 +1,41 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 a snapshot, overwriting if one already exists at path + block: + - name: Create a snapshot named 'snapshot_clobber' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_clobber" + snapshot_source: "/" + snapshot_conflict: "clobber" + state: "present" + register: result + - name: Snapshot 'snapshot_clobber' created + ansible.builtin.assert: + that: + - result is changed + + - name: Create a snapshot named 'snapshot_clobber' (no idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_clobber" + snapshot_source: "/" + snapshot_conflict: "clobber" + state: "present" + register: result + - name: Snapshot 'snapshot_clobber' created (no idempotency) + ansible.builtin.assert: + that: + - result is changed + +- name: Cleanup created snapshot + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_clobber" + state: "absent" diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_error.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_error.yml new file mode 100644 index 000000000..49d928b74 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_error.yml @@ -0,0 +1,42 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 a snapshot, erroring if one already exists at path + block: + - name: Create a snapshot named 'snapshot_error' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_error" + snapshot_source: "/" + snapshot_conflict: "error" + state: "present" + register: result + - name: Snapshot 'snapshot_error' created + ansible.builtin.assert: + that: + - result is changed + + - name: Create a snapshot named 'snapshot_error' (no idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_error" + snapshot_source: "/" + snapshot_conflict: "error" + state: "present" + register: result + ignore_errors: true + - name: Snapshot 'snapshot_error' created (no idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Cleanup created snapshot + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_error" + state: "absent" diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_skip.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_skip.yml new file mode 100644 index 000000000..07e65b133 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_snapshot_skip.yml @@ -0,0 +1,41 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 a snapshot if one does not already exist at path + block: + - name: Create a snapshot named 'snapshot_skip' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_skip" + snapshot_source: "/" + snapshot_conflict: "skip" + state: "present" + register: result + - name: Snapshot 'snapshot_skip' created + ansible.builtin.assert: + that: + - result is changed + + - name: Create a snapshot named 'snapshot_skip' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_skip" + snapshot_source: "/" + snapshot_conflict: "skip" + state: "present" + register: result + - name: Snapshot 'snapshot_skip' created (idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Cleanup created snapshot + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/snapshot_skip" + state: "absent" diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_default.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_default.yml new file mode 100644 index 000000000..f6eed9387 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_default.yml @@ -0,0 +1,99 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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: Change the default subvolume + block: + - name: Update filesystem default subvolume to '@' + community.general.btrfs_subvolume: + automount: Yes + default: True + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/@" + state: "present" + register: result + - name: Subvolume '@' set to default + ansible.builtin.assert: + that: + - result is changed + - name: Update filesystem default subvolume to '@' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + default: True + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/@" + state: "present" + register: result + - name: Subvolume '@' set to default (idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Revert the default subvolume + block: + - name: Revert filesystem default subvolume to '/' + community.general.btrfs_subvolume: + automount: Yes + default: True + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/" + state: "present" + register: result + - name: Subvolume '/' set to default + ansible.builtin.assert: + that: + - result is changed + - name: Revert filesystem default subvolume to '/' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + default: True + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/" + state: "present" + register: result + - name: Subvolume '/' set to default (idempotency) + ansible.builtin.assert: + that: + - result is not changed + + +- name: Change the default subvolume again + block: + - name: Update filesystem default subvolume to '@' + community.general.btrfs_subvolume: + automount: Yes + default: True + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/@" + state: "present" + register: result + - name: Subvolume '@' set to default + ansible.builtin.assert: + that: + - result is changed + +- name: Revert custom default subvolume to fs_tree root when deleted + block: + - name: Delete custom default subvolume '@' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/@" + state: "absent" + register: result + - name: Subvolume '@' deleted + ansible.builtin.assert: + that: + - result is changed + - name: Delete custom default subvolume '@' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/@" + state: "absent" + register: result + - name: Subvolume '@' deleted (idempotency) + ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_nested.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_nested.yml new file mode 100644 index 000000000..b706bf72a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_nested.yml @@ -0,0 +1,61 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 parent subvolume 'container' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container" + state: "present" + +- name: Create a nested subvolume + block: + - name: Create a subvolume named 'nested' inside 'container' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/nested" + state: "present" + register: result + - name: Subvolume 'container/nested' created + ansible.builtin.assert: + that: + - result is changed + - name: Create a subvolume named 'nested' inside 'container' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/nested" + state: "present" + register: result + - name: Subvolume 'container/nested' created (idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Remove a nested subvolume + block: + - name: Remove a subvolume named 'nested' inside 'container' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/nested" + state: "absent" + register: result + - name: Subvolume 'container/nested' removed + ansible.builtin.assert: + that: + - result is changed + - name: Remove a subvolume named 'nested' inside 'container' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/nested" + state: "absent" + register: result + - name: Subvolume 'container/nested' removed (idempotency) + ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_recursive.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_recursive.yml new file mode 100644 index 000000000..7e9f99007 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_recursive.yml @@ -0,0 +1,86 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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: Recursively create subvolumes + block: + - name: Create a subvolume named '/recursive/son/grandson' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/recursive/son/grandson" + recursive: Yes + state: "present" + register: result + - name: Subvolume named '/recursive/son/grandson' created + ansible.builtin.assert: + that: + - result is changed + + - name: Create a subvolume named '/recursive/son/grandson' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/recursive/son/grandson" + recursive: Yes + state: "present" + register: result + - name: Subvolume named '/recursive/son/grandson' created (idempotency) + ansible.builtin.assert: + that: + - result is not changed + + - name: Create a subvolume named '/recursive/daughter/granddaughter' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/recursive/daughter/granddaughter" + recursive: Yes + state: "present" + register: result + - name: Subvolume named '/recursive/son/grandson' created + ansible.builtin.assert: + that: + - result is changed + + - name: Create a subvolume named '/recursive/daughter/granddaughter' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/recursive/daughter/granddaughter" + recursive: Yes + state: "present" + register: result + - name: Subvolume named '/recursive/son/grandson' created (idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Recursively remove subvolumes + block: + - name: Remove subvolume '/recursive' and all descendents + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/recursive" + recursive: Yes + state: "absent" + register: result + - name: Subvolume '/recursive' removed + ansible.builtin.assert: + that: + - result is changed + + - name: Remove subvolume '/recursive' and all descendents (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/recursive" + recursive: Yes + state: "absent" + register: result + - name: Subvolume '/recursive' removed (idempotency) + ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_simple.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_simple.yml new file mode 100644 index 000000000..6cd214e74 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_simple.yml @@ -0,0 +1,54 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 a simple subvolume + block: + - name: Create a subvolume named 'simple' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/simple" + state: "present" + register: result + - name: Subvolume named 'simple' created + ansible.builtin.assert: + that: + - result is changed + - name: Create a subvolume named 'simple' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/simple" + state: "present" + register: result + - name: Subvolume named 'simple' created (idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Remove a simple subvolume + block: + - name: Remove a subvolume named 'simple' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/simple" + state: "absent" + register: result + - name: Subvolume named 'simple' removed + ansible.builtin.assert: + that: + - result is changed + - name: Remove a subvolume named 'simple' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/simple" + state: "absent" + register: result + - name: Subvolume named 'simple' removed (idempotency) + ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_whitespace.yml b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_whitespace.yml new file mode 100644 index 000000000..6a0147af6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/tasks/test_subvolume_whitespace.yml @@ -0,0 +1,62 @@ +--- +# Copyright (c) 2022, Gregory Furlong +# 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 a subvolume named 'container' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container" + state: "present" + +- name: Create a subvolume with whitespace in the name + block: + - name: Create a subvolume named 'container/my data' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/my data" + state: "present" + register: result + - name: Subvolume named 'container/my data' created + ansible.builtin.assert: + that: + - result is changed + - name: Create a subvolume named 'container/my data' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/my data" + state: "present" + register: result + - name: Subvolume named 'container/my data' created (idempotency) + ansible.builtin.assert: + that: + - result is not changed + +- name: Remove a subvolume with whitespace in the name + block: + - name: Remove a subvolume named 'container/my data' + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/my data" + state: "absent" + register: result + - name: Subvolume named 'container/my data' removed + ansible.builtin.assert: + that: + - result is changed + + - name: Remove a subvolume named 'container/my data' (idempotency) + community.general.btrfs_subvolume: + automount: Yes + filesystem_label: "{{ btrfs_subvolume_target_label }}" + name: "/container/my data" + state: "absent" + register: result + - name: Subvolume named 'container/my data' removed (idempotency) + ansible.builtin.assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/callback/inventory.yml b/ansible_collections/community/general/tests/integration/targets/callback/inventory.yml new file mode 100644 index 000000000..8e1a47e9a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback/inventory.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 + +all: + hosts: + testhost: + ansible_connection: local diff --git a/ansible_collections/community/general/tests/integration/targets/callback/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/callback/tasks/main.yml new file mode 100644 index 000000000..827217a53 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback/tasks/main.yml @@ -0,0 +1,100 @@ +--- +#################################################################### +# 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 playbook files + tempfile: + state: file + suffix: temp + loop: "{{ tests }}" + loop_control: + loop_var: test + label: "{{ test.name }}" + register: temporary_playbook_files + + - name: Set temporary playbook file content + copy: + content: "{{ test.playbook }}" + dest: "{{ temporary_playbook_files.results[test_idx].path }}" + loop: "{{ tests }}" + loop_control: + loop_var: test + index_var: test_idx + label: "{{ test.name }}" + + - name: Collect outputs + command: "ansible-playbook -i {{ inventory }} {{ playbook }}" + environment: "{{ test.environment }}" + loop: "{{ tests }}" + loop_control: + loop_var: test + label: "{{ test.name }}" + register: outputs + changed_when: false + vars: + inventory: "{{ role_path }}/inventory.yml" + playbook: " + {%- for result in temporary_playbook_files.results -%} + {%- if result.test.name == test.name -%} + {{- result.path -}} + {%- endif -%} + {%- endfor -%}" + + - name: Assert test output equals expected output + assert: + that: result.output.differences | length == 0 + loop: "{{ results }}" + loop_control: + loop_var: result + label: "{{ result.name }}" + register: assertions + vars: + results: >- + {%- set results = [] -%} + {%- for result in outputs.results -%} + {%- set differences = [] -%} + {%- for i in range([result.test.expected_output | count, result.stdout_lines | count] | max) -%} + {%- set line = "line_%s" | format(i+1) -%} + {%- set test_line = result.stdout_lines[i] | default(none) -%} + {%- set expected_lines = result.test.expected_output[i] | default(none) -%} + {%- if expected_lines is not string and expected_lines is not none -%} + {%- if test_line not in expected_lines -%} + {{- differences.append({ + line: { + 'expected_one_of': expected_lines, + 'got': test_line }}) -}} + {%- endif -%} + {%- else -%} + {%- if expected_lines != test_line -%} + {{- differences.append({ + line: { + 'expected': expected_lines, + 'got': test_line }}) -}} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + {{- results.append({ + 'name': result.test.name, + 'output': { + 'differences': differences, + 'expected': result.test.expected_output, + 'got': result.stdout_lines }}) -}} + {%- endfor -%} + {{- results -}} + + always: + - name: Remove temporary playbooks + file: + path: "{{ temporary_file.path }}" + state: absent + loop: "{{ temporary_playbook_files.results }}" + loop_control: + loop_var: temporary_file + label: "{{ temporary_file.test.name }}: {{ temporary_file.path }}" diff --git a/ansible_collections/community/general/tests/integration/targets/callback_diy/aliases b/ansible_collections/community/general/tests/integration/targets/callback_diy/aliases new file mode 100644 index 000000000..3e2dd244c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_diy/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_diy/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/callback_diy/tasks/main.yml new file mode 100644 index 000000000..fa468b52b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_diy/tasks/main.yml @@ -0,0 +1,462 @@ +--- +#################################################################### +# 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: Run tests + include_role: + name: callback + vars: + tests: + - name: Not using diy callback options + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set playbook_on_start_msg callback using environment variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + ANSIBLE_CALLBACK_DIY_PLAYBOOK_ON_START_MSG: "Sample output Sample playbook message" + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "Sample output Sample playbook message", + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set playbook_on_play_start_msg callback using play variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - name: Sample play name + hosts: testhost + gather_facts: false + vars: + ansible_callback_diy_playbook_on_play_start_msg: Sample output {{ ansible_callback_diy.play.name }} + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "Sample output Sample play name", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set playbook_on_task_start_msg callback using play variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + vars: + ansible_callback_diy_playbook_on_task_start_msg: Sample output {{ ansible_callback_diy.task.name }} + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "Sample output Sample task name", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set playbook_on_task_start_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + vars: + ansible_callback_diy_playbook_on_task_start_msg: Sample output {{ ansible_callback_diy.task.name }} + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "Sample output Sample task name", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set runner_on_ok_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + vars: + ansible_callback_diy_runner_on_ok_msg: Sample output {{ ansible_callback_diy.result.output.msg }} + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "Sample output sample debug msg", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set runner_on_failed_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + failed_when: true + ignore_errors: true + vars: + ansible_callback_diy_runner_on_failed_msg: Sample output Sample failure message + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "Sample output Sample failure message", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 " + ] + + - name: Set runner_on_skipped_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + when: false + vars: + ansible_callback_diy_runner_on_skipped_msg: Sample output Skipped {{ ansible_callback_diy.task.name }} + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "Sample output Skipped Sample task name", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 " + ] + + - name: Set runner_item_on_ok_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg {{ item }} + loop: + - sample item 1 + - sample item 2 + - sample item 3 + vars: + ansible_callback_diy_runner_item_on_ok_msg: Sample output Looping {{ ansible_callback_diy.result.output.msg }} + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "Sample output Looping sample debug msg sample item 1", + "Sample output Looping sample debug msg sample item 2", + "Sample output Looping sample debug msg sample item 3", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set runner_item_on_failed_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg {{ item }} + loop: + - sample item 1 + - sample item 2 + - sample item 3 + failed_when: item == 'sample item 2' + ignore_errors: true + vars: + ansible_callback_diy_runner_item_on_failed_msg: Sample output Looping sample failure message + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => (item=sample item 1) => {", + " \"msg\": \"sample debug msg sample item 1\"", + "}", + "Sample output Looping sample failure message", + "ok: [testhost] => (item=sample item 3) => {", + " \"msg\": \"sample debug msg sample item 3\"", + "}", + [ + # Apparently a bug was fixed in Ansible, as before it ran through with "All items completed" + "fatal: [testhost]: FAILED! => {\"msg\": \"All items completed\"}", + "fatal: [testhost]: FAILED! => {\"msg\": \"One or more items failed\"}", + ], + "...ignoring", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 " + ] + + - name: Set runner_item_on_skipped_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg {{ item }} + loop: + - sample item 1 + - sample item 2 + - sample item 3 + when: item != 'sample item 2' + vars: + ansible_callback_diy_runner_item_on_skipped_msg: Sample output Looping Skipped {{ ansible_callback_diy.result.output.item }} + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => (item=sample item 1) => {", + " \"msg\": \"sample debug msg sample item 1\"", + "}", + "Sample output Looping Skipped sample item 2", + "ok: [testhost] => (item=sample item 3) => {", + " \"msg\": \"sample debug msg sample item 3\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set playbook_on_stats_msg callback using play variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + vars: + ansible_callback_diy_playbook_on_stats_msg: |+2 + Sample output stats + =============================== + {% for key in ansible_callback_diy.stats | sort %} + {% set color_one = "" %} + {% set color_two = "" %} + {% if ansible_callback_diy.stats[key] %} + {% if key == 'ok' %} + {% set prefix = ' ' %} + {% set suffix = ' ' %} + {% elif key == 'changed' %} + {% set prefix = ' ' %} + {% set suffix = ' ' %} + {% elif key == 'processed' %} + {% set prefix = ' ' %} + {% set suffix = ' ' %} + {% elif key == 'skipped' %} + {% set prefix = ' ' %} + {% set suffix = ' ' %} + {% else %} + {% set prefix = "" %} + {% set suffix = "" %} + {% endif %} + {{ color_one }}{{ "%s%s%s" | format(prefix,key,suffix) }}{{ color_two }}: {{ ansible_callback_diy.stats[key] | to_nice_yaml }} + {% endif %} + {% endfor %} + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + " Sample output stats", + "===============================", + " ok : testhost: 1", + "", + " processed : testhost: 1" + ] + + - name: Suppress output on playbook_on_task_start_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + vars: + ansible_callback_diy_playbook_on_task_start_msg: '' + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Suppress output on runner_on_ok_msg callback using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + vars: + ansible_callback_diy_runner_on_ok_msg: '' + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Set runner_on_ok_msg_color using task variable + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.diy + playbook: !unsafe | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + vars: + ansible_callback_diy_runner_on_ok_msg: Sample output {{ ansible_callback_diy.result.output.msg }} + ansible_callback_diy_runner_on_ok_msg_color: blue + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "Sample output sample debug msg", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] diff --git a/ansible_collections/community/general/tests/integration/targets/callback_log_plays/aliases b/ansible_collections/community/general/tests/integration/targets/callback_log_plays/aliases new file mode 100644 index 000000000..343f119da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_log_plays/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/3 diff --git a/ansible_collections/community/general/tests/integration/targets/callback_log_plays/ping_log.yml b/ansible_collections/community/general/tests/integration/targets/callback_log_plays/ping_log.yml new file mode 100644 index 000000000..24f35f899 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_log_plays/ping_log.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 + +- hosts: localhost + gather_facts: false + tasks: + - ping: diff --git a/ansible_collections/community/general/tests/integration/targets/callback_log_plays/runme.sh b/ansible_collections/community/general/tests/integration/targets/callback_log_plays/runme.sh new file mode 100755 index 000000000..88eea1626 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_log_plays/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 + +# ANSIBLE_CALLBACK_WHITELIST has been deprecated in ansible-base 2.11, ANSIBLE_CALLBACKS_ENABLED should be used +export ANSIBLE_CALLBACK_WHITELIST="community.general.log_plays,${ANSIBLE_CALLBACK_WHITELIST:-}" +export ANSIBLE_CALLBACKS_ENABLED="community.general.log_plays,${ANSIBLE_CALLBACKS_ENABLED:-}" + +# run play, should create log and dir if needed +export ANSIBLE_LOG_FOLDER="logit" +ansible-playbook ping_log.yml -v "$@" +[[ -f "${ANSIBLE_LOG_FOLDER}/localhost" ]] + +# now force it to fail +export ANSIBLE_LOG_FOLDER="logit.file" +touch "${ANSIBLE_LOG_FOLDER}" +ansible-playbook ping_log.yml -v "$@" 2>&1| grep 'Failure using method (v2_runner_on_ok) in callback plugin' +[[ ! -f "${ANSIBLE_LOG_FOLDER}/localhost" ]] diff --git a/ansible_collections/community/general/tests/integration/targets/callback_yaml/aliases b/ansible_collections/community/general/tests/integration/targets/callback_yaml/aliases new file mode 100644 index 000000000..a27cf0e26 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_yaml/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/1 +needs/target/callback diff --git a/ansible_collections/community/general/tests/integration/targets/callback_yaml/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/callback_yaml/tasks/main.yml new file mode 100644 index 000000000..f3c36663d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_yaml/tasks/main.yml @@ -0,0 +1,101 @@ +--- +#################################################################### +# 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: Run tests + include_role: + name: callback + vars: + tests: + - name: Basic run + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.yaml + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Sample task name] ********************************************************", + "ok: [testhost] => ", + " msg: sample debug msg", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + - name: Test umlauts in multiline + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.yaml + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Umlaut output + debug: + msg: "äöü\néêè\nßï☺" + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Umlaut output] ***********************************************************", + "ok: [testhost] => ", + " msg: |-", + " äöü", + " éêè", + " ßï☺", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + - name: Test to_yaml + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.yaml + playbook: | + - hosts: testhost + gather_facts: false + vars: + data: | + line 1 + line 2 + line 3 + tasks: + - name: Test to_yaml + debug: + msg: "{{ '{{' }}'{{ '{{' }}'{{ '}}' }} data | to_yaml {{ '{{' }}'{{ '}}' }}'{{ '}}' }}" + # The above should be: msg: "{{ data | to_yaml }}" + # Unfortunately, the way Ansible handles templating, we need to do some funny 'escaping' tricks... + expected_output: [ + "", + "PLAY [testhost] ****************************************************************", + "", + "TASK [Test to_yaml] ************************************************************", + "ok: [testhost] => ", + " msg: |-", + " 'line 1", + " ", + " line 2", + " ", + " line 3", + " ", + " '", + "", + "PLAY RECAP *********************************************************************", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/aliases b/ansible_collections/community/general/tests/integration/targets/cargo/aliases new file mode 100644 index 000000000..9c7febe24 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cargo/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 +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/cargo/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cargo/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/cargo/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml new file mode 100644 index 000000000..bb22e27c0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.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 + +- import_tasks: setup.yml +- name: Set default environment + set_fact: + cargo_environment: {} +- name: Set special environment to work around cargo bugs + set_fact: + cargo_environment: + # See https://github.com/rust-lang/cargo/issues/10230#issuecomment-1201662729: + CARGO_NET_GIT_FETCH_WITH_CLI: "true" + when: has_cargo | default(false) and ansible_distribution == 'Alpine' +- block: + - import_tasks: test_general.yml + - import_tasks: test_version.yml + environment: "{{ cargo_environment }}" + when: has_cargo | 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 new file mode 100644 index 000000000..232658ab4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.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 + +- block: + - name: Install cargo + package: + name: cargo + state: present + - set_fact: + has_cargo: true + when: + - ansible_system != 'FreeBSD' + - ansible_distribution != 'MacOSX' + - ansible_distribution != 'RedHat' or ansible_distribution_version is version('8.0', '>=') + - ansible_distribution != 'CentOS' or ansible_distribution_version is version('7.0', '>=') + - ansible_distribution != 'Ubuntu' or ansible_distribution_version is version('18', '>=') + +- block: + - name: Install rust (containing cargo) + package: + name: rust + state: present + - set_fact: + has_cargo: true + when: + - ansible_system == 'FreeBSD' and ansible_distribution_version is version('13.0', '>') diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_general.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_general.yml new file mode 100644 index 000000000..2bffa08f0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_general.yml @@ -0,0 +1,35 @@ +--- +# 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 application helloworld is uninstalled + community.general.cargo: + state: absent + name: helloworld + register: uninstall_absent_helloworld + +- name: Install application helloworld + community.general.cargo: + name: helloworld + register: install_absent_helloworld + +- name: Install application helloworld again + community.general.cargo: + name: helloworld + register: install_present_helloworld + ignore_errors: true + +- name: Uninstall application helloworld + community.general.cargo: + state: absent + name: helloworld + register: uninstall_present_helloworld + +- name: Check assertions helloworld + assert: + that: + - uninstall_absent_helloworld is not changed + - install_absent_helloworld is changed + - install_present_helloworld is not changed + - uninstall_present_helloworld is changed diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_version.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_version.yml new file mode 100644 index 000000000..c1ab8e198 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_version.yml @@ -0,0 +1,50 @@ +--- +# 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-yliu 0.1.0 + community.general.cargo: + name: helloworld-yliu + version: 0.1.0 + register: install_helloworld_010 + +- name: Install application helloworld-yliu 0.1.0 (idempotent) + community.general.cargo: + name: helloworld-yliu + version: 0.1.0 + register: install_helloworld_010_idem + +- name: Upgrade helloworld-yliu 0.1.0 + community.general.cargo: + name: helloworld-yliu + state: latest + register: upgrade_helloworld_010 + +- name: Upgrade helloworld-yliu 0.1.0 (idempotent) + community.general.cargo: + name: helloworld-yliu + state: latest + register: upgrade_helloworld_010_idem + +- name: Downgrade helloworld-yliu 0.1.0 + community.general.cargo: + name: helloworld-yliu + version: 0.1.0 + register: downgrade_helloworld_010 + +- name: Downgrade helloworld-yliu 0.1.0 (idempotent) + community.general.cargo: + name: helloworld-yliu + version: 0.1.0 + register: downgrade_helloworld_010_idem + +- name: Check assertions helloworld-yliu + assert: + that: + - install_helloworld_010 is changed + - install_helloworld_010_idem is not changed + - upgrade_helloworld_010 is changed + - upgrade_helloworld_010_idem is not changed + - downgrade_helloworld_010 is changed + - downgrade_helloworld_010_idem is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/aliases b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/aliases new file mode 100644 index 000000000..bec4d21af --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/aliases @@ -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 + +azp/posix/1 +destructive +skip/aix +skip/osx +skip/macos +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/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/cloud_init_data_facts/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml new file mode 100644 index 000000000..40e762d68 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml @@ -0,0 +1,68 @@ +--- +#################################################################### +# 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: test cloud-init + # TODO: check for a workaround + # install 'cloud-init'' failed: dpkg-divert: error: `diversion of /etc/init/ureadahead.conf + # to /etc/init/ureadahead.conf.disabled by cloud-init' clashes with `local diversion of + # /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. + 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_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 + block: + - name: setup install cloud-init + package: + name: + - cloud-init + - udev + + - name: Ensure systemd-network user exists + user: + name: systemd-network + state: present + when: ansible_distribution == 'Fedora' and ansible_distribution_major_version|int >= 37 + + - name: setup run cloud-init + service: + name: cloud-init-local + state: restarted + + - name: test gather cloud-init facts in check mode + cloud_init_data_facts: + check_mode: true + register: result + - name: verify test gather cloud-init facts in check mode + assert: + that: + - result.cloud_init_data_facts.status.v1 is defined + - result.cloud_init_data_facts.status.v1.stage is defined + - not result.cloud_init_data_facts.status.v1.stage + - cloud_init_data_facts.status.v1 is defined + - cloud_init_data_facts.status.v1.stage is defined + - not cloud_init_data_facts.status.v1.stage + + - name: test gather cloud-init facts + cloud_init_data_facts: + register: result + - name: verify test gather cloud-init facts + assert: + that: + - result.cloud_init_data_facts.status.v1 is defined + - result.cloud_init_data_facts.status.v1.stage is defined + - not result.cloud_init_data_facts.status.v1.stage + - cloud_init_data_facts.status.v1 is defined + - cloud_init_data_facts.status.v1.stage is defined + - not cloud_init_data_facts.status.v1.stage diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/aliases b/ansible_collections/community/general/tests/integration/targets/cmd_runner/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/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/cmd_runner/library/cmd_echo.py b/ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py new file mode 100644 index 000000000..cd8766264 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py @@ -0,0 +1,55 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Alexei Znamensky +# 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 + + +DOCUMENTATION = "" + +EXAMPLES = "" + +RETURN = "" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt + + +def main(): + module = AnsibleModule( + argument_spec=dict( + 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"), + ), + supports_check_mode=True, + ) + p = module.params + + info = None + + arg_formats = {} + for arg, fmt_spec in p['arg_formats'].items(): + func = getattr(fmt, fmt_spec['func']) + args = fmt_spec.get("args", []) + + arg_formats[arg] = func(*args) + + runner = CmdRunner(module, ['echo', '--'], arg_formats=arg_formats) + + with runner.context(p['arg_order'], check_mode_skip=p['check_mode_skip']) as ctx: + result = ctx.run(**p['arg_values']) + info = ctx.run_info + check = "check" + rc, out, err = result if result is not None else (None, None, None) + + module.exit_json(rc=rc, out=out, err=err, info=info) + + +if __name__ == '__main__': + main() 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 new file mode 100644 index 000000000..36ab039f0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml @@ -0,0 +1,8 @@ +# Copyright (c) 2022, Alexei Znamensky +# 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: parameterized test cmd_echo + ansible.builtin.include_tasks: + file: test_cmd_echo.yml + loop: "{{ cmd_echo_tests }}" 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 new file mode 100644 index 000000000..1c2caf2b5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.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 + +- name: test cmd_echo [{{ item.name }}] + cmd_echo: + 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) }}" + register: test_result + check_mode: "{{ item.check_mode|default(omit) }}" + ignore_errors: "{{ item.expect_error|default(omit) }}" + +- name: check results [{{ item.name }}] + 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 new file mode 100644 index 000000000..7f0027d49 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Alexei Znamensky +# 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 + +cmd_echo_tests: + - name: set aa and bb value + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + bb: + func: as_bool + args: [--bb-here] + arg_order: 'aa bb' + arg_values: + bb: true + aa: 11 + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=11 --bb-here\n" + - test_result.err == "" + + - name: default aa value + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + bb: + func: as_bool + args: [--bb-here] + arg_order: ['aa', 'bb'] + arg_values: + aa: 43 + bb: true + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=43 --bb-here\n" + - test_result.err == "" + + - name: implicit aa format + arg_formats: + bb: + func: as_bool + args: [--bb-here] + arg_order: ['aa', 'bb'] + arg_values: + bb: true + aa: 1984 + assertions: + - test_result.rc == 0 + - test_result.out == "-- --aa 1984 --bb-here\n" + - test_result.err == "" + + - name: missing bb format + arg_order: ['aa', 'bb'] + arg_values: + bb: true + aa: 1984 + expect_error: true + assertions: + - test_result is failed + - test_result.rc == 1 + - '"out" not in test_result' + - '"err" not in test_result' + - >- + "MissingArgumentFormat: Cannot find format for parameter bb" + in test_result.module_stderr + + - name: missing bb value + arg_formats: + bb: + func: as_bool + args: [--bb-here] + arg_order: 'aa bb' + aa: 1984 + expect_error: true + assertions: + - test_result is failed + - test_result.rc == 1 + - '"out" not in test_result' + - '"err" not in test_result' + - >- + "MissingArgumentValue: Cannot find value for parameter bb" + in test_result.module_stderr + + - name: set aa and bb value with check_mode on + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + bb: + func: as_bool + args: [--bb-here] + arg_order: 'aa bb' + arg_values: + bb: true + aa: 11 + check_mode: true + assertions: + - test_result.rc == 0 + - test_result.out == "-- --answer=11 --bb-here\n" + - test_result.err == "" + + - name: set aa and bb value with check_mode and check_mode_skip on + arg_formats: + aa: + func: as_opt_eq_val + args: [--answer] + bb: + func: as_bool + args: [--bb-here] + arg_order: 'aa bb' + arg_values: + bb: true + check_mode_skip: true + aa: 11 + check_mode: true + expect_error: true # because if result contains rc != 0, ansible assumes error + assertions: + - test_result.rc == None + - test_result.out == None + - test_result.err == None diff --git a/ansible_collections/community/general/tests/integration/targets/connection/aliases b/ansible_collections/community/general/tests/integration/targets/connection/aliases new file mode 100644 index 000000000..a02a2d61a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection/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 + +hidden diff --git a/ansible_collections/community/general/tests/integration/targets/connection/test.sh b/ansible_collections/community/general/tests/integration/targets/connection/test.sh new file mode 100755 index 000000000..793a85dd3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection/test.sh @@ -0,0 +1,16 @@ +#!/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 + +[ -f "${INVENTORY}" ] + +# Run connection tests with both the default and C locale. + +ansible-playbook test_connection.yml -i "${INVENTORY}" "$@" + +if ansible --version | grep ansible | grep -E ' 2\.(9|10|11|12|13)\.'; then + LC_ALL=C LANG=C ansible-playbook test_connection.yml -i "${INVENTORY}" "$@" +fi diff --git a/ansible_collections/community/general/tests/integration/targets/connection/test_connection.yml b/ansible_collections/community/general/tests/integration/targets/connection/test_connection.yml new file mode 100644 index 000000000..bb0a99399 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection/test_connection.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 + +- hosts: "{{ target_hosts }}" + gather_facts: false + serial: 1 + tasks: + + ### raw with unicode arg and output + + - name: raw with unicode arg and output + raw: echo 汉语 + register: command + - name: check output of raw with unicode arg and output + assert: + that: + - "'汉语' in command.stdout" + - command is changed # as of 2.2, raw should default to changed: true for consistency w/ shell/command/script modules + + ### copy local file with unicode filename and content + + - name: create local file with unicode filename and content + local_action: lineinfile dest={{ local_tmp }}-汉语/汉语.txt create=true line=汉语 + - name: remove remote file with unicode filename and content + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语/汉语.txt state=absent" + - name: create remote directory with unicode name + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 state=directory" + - name: copy local file with unicode filename and content + action: "{{ action_prefix }}copy src={{ local_tmp }}-汉语/汉语.txt dest={{ remote_tmp }}-汉语/汉语.txt" + + ### fetch remote file with unicode filename and content + + - name: remove local file with unicode filename and content + local_action: file path={{ local_tmp }}-汉语/汉语.txt state=absent + - name: fetch remote file with unicode filename and content + fetch: src={{ remote_tmp }}-汉语/汉语.txt dest={{ local_tmp }}-汉语/汉语.txt fail_on_missing=true validate_checksum=true flat=true + + ### remove local and remote temp files + + - name: remove local temp file + local_action: file path={{ local_tmp }}-汉语 state=absent + - name: remove remote temp file + action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 state=absent" + + ### test wait_for_connection plugin + - ansible.builtin.wait_for_connection: diff --git a/ansible_collections/community/general/tests/integration/targets/connection_chroot/aliases b/ansible_collections/community/general/tests/integration/targets/connection_chroot/aliases new file mode 100644 index 000000000..38138ee4d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_chroot/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 +needs/root +skip/macos # Skipped due to limitation of macOS 10.15 SIP, please read https://github.com/ansible-collections/community.general/issues/1017#issuecomment-755088895 diff --git a/ansible_collections/community/general/tests/integration/targets/connection_chroot/runme.sh b/ansible_collections/community/general/tests/integration/targets/connection_chroot/runme.sh new file mode 100755 index 000000000..9f31da64d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_chroot/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_chroot/test_connection.inventory b/ansible_collections/community/general/tests/integration/targets/connection_chroot/test_connection.inventory new file mode 100644 index 000000000..126b29c8a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_chroot/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 + +[chroot] +chroot-pipelining ansible_ssh_pipelining=true +chroot-no-pipelining ansible_ssh_pipelining=false +[chroot:vars] +ansible_host=/ +ansible_connection=community.general.chroot +ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/ansible_collections/community/general/tests/integration/targets/connection_jail/aliases b/ansible_collections/community/general/tests/integration/targets/connection_jail/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_jail/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/connection_jail/runme.sh b/ansible_collections/community/general/tests/integration/targets/connection_jail/runme.sh new file mode 100755 index 000000000..9f31da64d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_jail/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_jail/test_connection.inventory b/ansible_collections/community/general/tests/integration/targets/connection_jail/test_connection.inventory new file mode 100644 index 000000000..995c32444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_jail/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 + +[jail] +jail-pipelining ansible_ssh_pipelining=true +jail-no-pipelining ansible_ssh_pipelining=false +[jail:vars] +ansible_host=freebsd_10_2 +ansible_connection=community.general.jail +ansible_python_interpreter=/usr/local/bin/python diff --git a/ansible_collections/community/general/tests/integration/targets/connection_lxc/aliases b/ansible_collections/community/general/tests/integration/targets/connection_lxc/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_lxc/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/connection_lxc/runme.sh b/ansible_collections/community/general/tests/integration/targets/connection_lxc/runme.sh new file mode 100755 index 000000000..9f31da64d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_lxc/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_lxc/test_connection.inventory b/ansible_collections/community/general/tests/integration/targets/connection_lxc/test_connection.inventory new file mode 100644 index 000000000..cfcd7a32f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_lxc/test_connection.inventory @@ -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 + +[lxc] +lxc-pipelining ansible_ssh_pipelining=true +lxc-no-pipelining ansible_ssh_pipelining=false +[lxc:vars] +# 1. install lxc +# 2. install python2-lxc +# $ pip install git+https://github.com/lxc/python2-lxc.git +# 3. create container: +# $ sudo lxc-create -t download -n centos-7-amd64 -- -d centos -r 7 -a amd64 +# 4. start container: +# $ sudo lxc-start -n centos-7-amd64 -d +# 5. run test: +# $ sudo -E make test_connection_lxc +# 6. stop container +# $ sudo lxc-stop -n centos-7-amd64 +ansible_host=centos-7-amd64 +ansible_connection=community.general.lxc diff --git a/ansible_collections/community/general/tests/integration/targets/connection_lxd/aliases b/ansible_collections/community/general/tests/integration/targets/connection_lxd/aliases new file mode 100644 index 000000000..5a0c47032 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_lxd/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_lxd/runme.sh b/ansible_collections/community/general/tests/integration/targets/connection_lxd/runme.sh new file mode 100755 index 000000000..9f31da64d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_lxd/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_lxd/test_connection.inventory b/ansible_collections/community/general/tests/integration/targets/connection_lxd/test_connection.inventory new file mode 100644 index 000000000..d2d2c10e3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_lxd/test_connection.inventory @@ -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 + +[lxd] +lxd-pipelining ansible_ssh_pipelining=true +lxd-no-pipelining ansible_ssh_pipelining=false +[lxd:vars] +ansible_host=centos-7-amd64 +ansible_connection=community.general.lxd diff --git a/ansible_collections/community/general/tests/integration/targets/connection_posix/aliases b/ansible_collections/community/general/tests/integration/targets/connection_posix/aliases new file mode 100644 index 000000000..44561e2ff --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_posix/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 + +needs/target/connection +hidden diff --git a/ansible_collections/community/general/tests/integration/targets/connection_posix/test.sh b/ansible_collections/community/general/tests/integration/targets/connection_posix/test.sh new file mode 100755 index 000000000..9f31da64d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/connection_posix/test.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/consul/aliases b/ansible_collections/community/general/tests/integration/targets/consul/aliases new file mode 100644 index 000000000..d9cf1eb9c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/consul/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/aix +skip/macos # cannot simply create binaries in system locations on newer macOS versions diff --git a/ansible_collections/community/general/tests/integration/targets/consul/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/consul/meta/main.yml new file mode 100644 index 000000000..0909be206 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/consul/meta/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 + +dependencies: + - setup_pkg_mgr + - setup_openssl + - setup_remote_tmp_dir + - setup_remote_constraints 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 new file mode 100644 index 000000000..543668964 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml @@ -0,0 +1,177 @@ +--- +# 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: list sessions + consul_session: + state: list + register: result + +- assert: + that: + - result is changed + - "'sessions' in result" + +- name: create a session + consul_session: + state: present + name: testsession + register: result + +- assert: + that: + - result is changed + - result['name'] == 'testsession' + - "'session_id' in result" + +- set_fact: + session_id: "{{ result['session_id'] }}" + +- name: list sessions after creation + consul_session: + state: list + register: result + +- set_fact: + session_count: "{{ result['sessions'] | length }}" + +- assert: + that: + - result is changed + # selectattr not available on Jinja 2.2 provided by CentOS 6 + # hence the two following tasks (set_fact/assert) are used + # - (result['sessions'] | selectattr('ID', 'match', '^' ~ session_id ~ '$') | first)['Name'] == 'testsession' + +- name: search created session + set_fact: + test_session_found: true + loop: "{{ result['sessions'] }}" + when: "item.get('ID') == session_id and item.get('Name') == 'testsession'" + +- name: ensure session was created + assert: + that: + - test_session_found|default(False) + +- name: fetch info about a session + consul_session: + state: info + id: '{{ session_id }}' + register: result + +- assert: + that: + - result is changed + +- name: ensure 'id' parameter is required when state=info + consul_session: + state: info + name: test + register: result + ignore_errors: true + +- assert: + 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 + id: '{{ session_id }}' + register: result + +- assert: + that: + - result is changed + +- name: list sessions after deletion + consul_session: + state: list + register: result + +- assert: + that: + - result is changed + # selectattr and equalto not available on Jinja 2.2 provided by CentOS 6 + # hence the two following tasks (command/assert) are used + # - (result['sessions'] | selectattr('ID', 'equalto', session_id) | list | length) == 0 + +- name: search deleted session + command: echo 'session found' + loop: "{{ result['sessions'] }}" + when: "item.get('ID') == session_id and item.get('Name') == 'testsession'" + register: search_deleted + +- name: ensure session was deleted + assert: + that: + - search_deleted is skipped # each iteration is skipped + - search_deleted is not changed # and then unchanged + +- name: ensure session can be created with a ttl + consul_session: + state: present + name: session-with-ttl + ttl: 180 # sec + register: result + +- assert: + that: + - result is changed + - result['ttl'] == 180 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 new file mode 100644 index 000000000..a2b63ac95 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml @@ -0,0 +1,89 @@ +--- +#################################################################### +# 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 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_cmd: '{{ remote_tmp_dir }}/consul' + block: + - name: Install requests<2.20 (CentOS/RHEL 6) + pip: + name: requests<2.20 + extra_args: "-c {{ remote_constraints }}" + register: result + until: result is success + when: ansible_distribution_file_variety|default() == 'RedHat' and ansible_distribution_major_version is version('6', '<=') + - name: Install python-consul + pip: + name: python-consul + extra_args: "-c {{ remote_constraints }}" + register: result + until: result is success + - name: Generate privatekey + community.crypto.openssl_privatekey: + path: '{{ remote_tmp_dir }}/privatekey.pem' + - name: Generate CSR + community.crypto.openssl_csr: + path: '{{ remote_tmp_dir }}/csr.csr' + privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem' + subject: + commonName: localhost + - name: Generate selfsigned certificate + register: selfsigned_certificate + community.crypto.x509_certificate: + path: '{{ remote_tmp_dir }}/cert.pem' + csr_path: '{{ remote_tmp_dir }}/csr.csr' + privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem' + provider: selfsigned + selfsigned_digest: sha256 + - name: Install unzip + package: + name: unzip + register: result + until: result is success + when: ansible_distribution != "MacOSX" + - assert: + that: ansible_architecture in ['i386', 'x86_64', 'amd64'] + - set_fact: + consul_arch: '386' + when: ansible_architecture == 'i386' + - set_fact: + consul_arch: amd64 + when: ansible_architecture in ['x86_64', 'amd64'] + - name: Download consul binary + unarchive: + src: '{{ consul_uri }}' + dest: '{{ remote_tmp_dir }}' + remote_src: true + register: result + until: result is success + - vars: + remote_dir: '{{ echo_remote_tmp_dir.stdout }}' + block: + - command: echo {{ remote_tmp_dir }} + register: echo_remote_tmp_dir + - name: Create configuration file + template: + src: consul_config.hcl.j2 + 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 2>&1 & + - name: Create some data + command: '{{ consul_cmd }} kv put data/value{{ item }} foo{{ item }}' + loop: + - 1 + - 2 + - 3 + - import_tasks: consul_session.yml + always: + - name: Kill consul process + shell: kill $(cat {{ remote_tmp_dir }}/consul.pid) + ignore_errors: true 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 new file mode 100644 index 000000000..96da5d664 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j2 @@ -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 +#} +# {{ ansible_managed }} +server = true +pid_file = "{{ remote_dir }}/consul.pid" +ports { + http = 8500 + https = 8501 +} +key_file = "{{ remote_dir }}/privatekey.pem" +cert_file = "{{ remote_dir }}/cert.pem" diff --git a/ansible_collections/community/general/tests/integration/targets/copr/aliases b/ansible_collections/community/general/tests/integration/targets/copr/aliases new file mode 100644 index 000000000..ed3c1af00 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/copr/aliases @@ -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 + +azp/posix/1 +needs/root +skip/macos +skip/osx +skip/freebsd 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 new file mode 100644 index 000000000..0e4651724 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml @@ -0,0 +1,160 @@ +--- +# 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 + +- when: + # Fedora or RHEL >= 8 + # This module requires the dnf module which is not available on RHEL 7. + - > + 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 + copr: + host: copr.fedorainfracloud.org + state: enabled + name: '{{ copr_fullname }}' + chroot: "{{ copr_chroot }}" + register: result + + - name: assert that the copr project was enabled + assert: + that: + - 'result is changed' + - result.msg == 'enabled' + - result.info == 'Please note that this repository is not part of the main distribution' + + - name: enable copr project + check_mode: true + copr: + state: enabled + name: '{{ copr_fullname }}' + chroot: '{{ copr_chroot }}' + register: result + + - name: assert that the copr project was enabled + assert: + that: + - result is not changed + - result.msg == 'enabled' + + - name: Ensure the repo is installed and enabled | slurp + register: result + ansible.builtin.slurp: + src: "{{ copr_repofile }}" + + - name: Ensure the repo is installed and enabled + vars: + content: "{{ result.content | b64decode }}" + _baseurl: "{{ 'https://download.copr.fedorainfracloud.org/results/gotmax23/community.general.copr_integration_tests' | regex_escape }}" + baseurl: "{{ content | regex_search('baseurl=' ~ _baseurl) }}" + block: + - ansible.builtin.debug: + var: content + - ansible.builtin.debug: + var: baseurl + - name: Ensure the repo is installed and enabled + ansible.builtin.assert: + that: + - "'enabled=1' in content" + - baseurl | length > 0 + + - name: Install test package from Copr + when: + # Copr does not build new packages for EOL Fedoras. + - > + not (ansible_distribution == 'Fedora' and + ansible_distribution_major_version | int < 35) + block: + - name: install test package from the copr + ansible.builtin.package: + update_cache: true + name: copr-module-integration-dummy-package + + - name: uninstall test package + register: result + ansible.builtin.package: + name: copr-module-integration-dummy-package + state: absent + + - name: check uninstall test package + ansible.builtin.assert: + that: result.changed | bool + + - name: remove copr project + copr: + state: absent + name: '{{ copr_fullname }}' + register: result + + - name: assert that the copr project was removed + assert: + that: + - 'result is changed' + - result.msg == 'absent' + + - name: Ensure the repo file was removed | stat + register: result + ansible.builtin.stat: + dest: "{{ copr_repofile }}" + + - name: Ensure the repo file was removed + ansible.builtin.assert: + that: not result.stat.exists | bool + + - name: disable copr project + copr: + state: disabled + name: '{{ copr_fullname }}' + chroot: '{{ copr_chroot }}' + register: result + + - name: assert that the copr project was disabled + assert: + that: + - 'result is changed' + - result.msg == 'disabled' + + - name: Ensure the repo is installed but disabled | slurp + register: result + ansible.builtin.slurp: + src: "{{ copr_repofile }}" + + - name: Ensure the repo is installed but disabled + vars: + content: "{{ result.content | b64decode }}" + _baseurl: "{{ 'https://download.copr.fedorainfracloud.org/results/gotmax23/community.general.copr_integration_tests' | regex_escape }}" + baseurl: "{{ content | regex_search('baseurl=' ~ _baseurl) }}" + block: + - ansible.builtin.debug: + var: content + - ansible.builtin.debug: + var: baseurl + - name: Ensure the repo is installed but disabled + ansible.builtin.assert: + that: + - "'enabled=0' in content" + - baseurl | length > 0 + + always: + - name: clean up + ignore_errors: true + copr: + host: copr.fedorainfracloud.org + state: absent + name: '{{ copr_fullname }}' + chroot: '{{ copr_chroot }}' + + - name: cleanup test package + ansible.builtin.package: + name: copr-module-integration-dummy-package + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/copr/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/copr/vars/main.yml new file mode 100644 index 000000000..a37a44d47 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/copr/vars/main.yml @@ -0,0 +1,15 @@ +# Copyright (c) 2022 Maxwell G +# 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 +--- +copr_host: copr.fedorainfracloud.org +copr_namespace: gotmax23 +copr_name: community.general.copr_integration_tests +copr_fullname: '{{ copr_namespace }}/{{ copr_name }}' +copr_repofile: '/etc/yum.repos.d/_copr:{{ copr_host }}:{{ copr_namespace }}:{{ copr_name }}.repo' + +# TODO: Fix chroot autodetection so this isn't necessary +_copr_chroot_fedora: "fedora-rawhide-x86_64" +_copr_chroot_rhelish: "epel-{{ ansible_distribution_major_version }}-x86_64" +copr_chroot: "{{ _copr_chroot_fedora if ansible_distribution == 'Fedora' else _copr_chroot_rhelish }}" diff --git a/ansible_collections/community/general/tests/integration/targets/cpanm/aliases b/ansible_collections/community/general/tests/integration/targets/cpanm/aliases new file mode 100644 index 000000000..d30ba06b6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cpanm/aliases @@ -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 + +azp/posix/3 +destructive +skip/macos +skip/osx +skip/freebsd +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/cpanm/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/cpanm/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cpanm/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/cpanm/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml new file mode 100644 index 000000000..c9adc1ca6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml @@ -0,0 +1,65 @@ +# Copyright (c) 2020, Berkhan Berkdemir +# Copyright (c) 2021, Alexei Znamensky +# 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 for non-supported platforms + meta: end_play + when: + - (ansible_os_family != "RedHat" or ansible_distribution_major_version|int < 7) + - ansible_os_family != "Debian" + +- name: install perl development package for Red Hat family + package: + name: + - perl-devel + - perl-App-cpanminus + state: present + become: true + when: ansible_os_family == "RedHat" + +- name: install perl development package for Debian family + package: + name: + - cpanminus + state: present + become: true + when: ansible_os_family == "Debian" + +- name: install a Perl package + cpanm: + name: JSON + notest: true + register: install_perl_package_result + +- name: assert package is installed + assert: + that: + - install_perl_package_result is changed + - install_perl_package_result is not failed + +- name: install same Perl package + cpanm: + name: JSON + notest: true + register: install_same_perl_package_result + +- name: assert same package is installed + assert: + that: + - install_same_perl_package_result is not changed + - install_same_perl_package_result is not failed + +- name: install a Perl package with version operator + cpanm: + name: JSON + version: "@4.01" + notest: true + mode: new + register: install_perl_package_with_version_op_result + +- name: assert package with version operator is installed + assert: + that: + - install_perl_package_with_version_op_result is changed + - install_perl_package_with_version_op_result is not failed diff --git a/ansible_collections/community/general/tests/integration/targets/cronvar/aliases b/ansible_collections/community/general/tests/integration/targets/cronvar/aliases new file mode 100644 index 000000000..e9ef7265d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cronvar/aliases @@ -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 + +azp/posix/3 +destructive +skip/aix +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/cronvar/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/cronvar/defaults/main.yml new file mode 100644 index 000000000..11ef47d9d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cronvar/defaults/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 + +cron_config_path: /etc/cron.d diff --git a/ansible_collections/community/general/tests/integration/targets/cronvar/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/cronvar/meta/main.yml new file mode 100644 index 000000000..92d116f2a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cronvar/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_cron diff --git a/ansible_collections/community/general/tests/integration/targets/cronvar/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cronvar/tasks/main.yml new file mode 100644 index 000000000..73ec41abc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/cronvar/tasks/main.yml @@ -0,0 +1,124 @@ +--- +#################################################################### +# 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: Ensure /etc/cron.d directory exists + file: + path: /etc/cron.d + state: directory + +- name: Create EMAIL cron var + cronvar: + name: EMAIL + value: doug@ansibmod.con.com + register: create_cronvar1 + +- name: Create EMAIL cron var again + cronvar: + name: EMAIL + value: doug@ansibmod.con.com + register: create_cronvar2 + +- name: Check cron var value + shell: crontab -l -u root | grep -c EMAIL=doug@ansibmod.con.com + register: varcheck1 + +- name: Modify EMAIL cron var + cronvar: + name: EMAIL + value: jane@ansibmod.con.com + register: create_cronvar3 + +- name: Check cron var value again + shell: crontab -l -u root | grep -c EMAIL=jane@ansibmod.con.com + register: varcheck2 + +- name: Remove EMAIL cron var + cronvar: + name: EMAIL + state: absent + register: remove_cronvar1 + +- name: Remove EMAIL cron var again + cronvar: + name: EMAIL + state: absent + register: remove_cronvar2 + +- name: Check cron var value again + shell: crontab -l -u root | grep -c EMAIL + register: varcheck3 + failed_when: varcheck3.rc == 0 + +- name: Add cron var to custom file + cronvar: + name: TESTVAR + value: somevalue + cron_file: cronvar_test + register: custom_cronfile1 + +- name: Add cron var to custom file again + cronvar: + name: TESTVAR + value: somevalue + cron_file: cronvar_test + register: custom_cronfile2 + +- name: Check cron var value in custom file + command: grep -c TESTVAR=somevalue {{ cron_config_path }}/cronvar_test + register: custom_varcheck1 + +- name: Change cron var in custom file + cronvar: + name: TESTVAR + value: newvalue + cron_file: cronvar_test + register: custom_cronfile3 + +- name: Check cron var value in custom file + command: grep -c TESTVAR=newvalue {{ cron_config_path }}/cronvar_test + register: custom_varcheck2 + +- name: Remove cron var from custom file + cronvar: + name: TESTVAR + value: newvalue + cron_file: cronvar_test + state: absent + register: custom_remove_cronvar1 + +- name: Remove cron var from custom file again + cronvar: + name: TESTVAR + value: newvalue + cron_file: cronvar_test + state: absent + register: custom_remove_cronvar2 + +- name: Check cron var value + command: grep -c TESTVAR=newvalue {{ cron_config_path }}/cronvar_test + register: custom_varcheck3 + failed_when: custom_varcheck3.rc == 0 + +- name: Ensure cronvar tasks did the right thing + assert: + that: + - create_cronvar1 is changed + - create_cronvar2 is not changed + - create_cronvar3 is changed + - remove_cronvar1 is changed + - remove_cronvar2 is not changed + - varcheck1.stdout == '1' + - varcheck2.stdout == '1' + - varcheck3.stdout == '0' + - custom_remove_cronvar1 is changed + - custom_remove_cronvar2 is not changed + - custom_varcheck1.stdout == '1' + - custom_varcheck2.stdout == '1' + - custom_varcheck3.stdout == '0' diff --git a/ansible_collections/community/general/tests/integration/targets/deploy_helper/aliases b/ansible_collections/community/general/tests/integration/targets/deploy_helper/aliases new file mode 100644 index 000000000..afda346c4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/deploy_helper/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/1 diff --git a/ansible_collections/community/general/tests/integration/targets/deploy_helper/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/deploy_helper/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/deploy_helper/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/deploy_helper/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml new file mode 100644 index 000000000..fdd8bd87b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml @@ -0,0 +1,158 @@ +--- +#################################################################### +# 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: record the output directory + set_fact: deploy_helper_test_root={{remote_tmp_dir}}/deploy_helper_test_root + +- name: State=query with default parameters + deploy_helper: path={{ deploy_helper_test_root }} state=query +- name: Assert State=query with default parameters + 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.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 }}'" + +- 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}}'" + +- 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 +- name: Assert State=query with absolute overridden paths + assert: + that: + - "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}}'" + +- name: State=query with overridden unfinished_filename + deploy_helper: path={{ deploy_helper_test_root }} unfinished_filename=UNFINISHED_DEPLOY state=query +- name: Assert State=query with overridden unfinished_filename + assert: + that: + - "'UNFINISHED_DEPLOY' == deploy_helper.unfinished_filename" + +# Remove the root folder just in case it exists +- file: path={{ deploy_helper_test_root }} state=absent + +- name: State=present with default parameters + deploy_helper: path={{ deploy_helper_test_root }} state=present +- stat: path={{ deploy_helper.releases_path }} + register: releases_path +- stat: path={{ deploy_helper.shared_path }} + register: shared_path +- name: Assert State=present with default parameters + assert: + that: + - "releases_path.stat.exists" + - "shared_path.stat.exists" + +# Setup older releases for tests +- file: path={{ deploy_helper.releases_path }}/{{ item }} state=directory + with_items: ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh'] +# Setup the new release +- file: path={{ deploy_helper.new_release_path }} state=directory +# Add a buildfile, just like in a real deploy +- copy: content='' dest={{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }} +# Add a buildfile, to an older deploy +- copy: content='' dest={{ deploy_helper.releases_path }}/third/{{ deploy_helper.unfinished_filename }} + +- name: State=finalize with default parameters + deploy_helper: path={{ deploy_helper_test_root }} release={{ deploy_helper.new_release }} state=finalize +- stat: path={{ deploy_helper.current_path }} + register: current_path +- stat: path={{ deploy_helper.current_path }}/DEPLOY_UNFINISHED + register: current_path_unfinished_filename +- name: Assert State=finalize with default parameters + assert: + that: + - "current_path.stat.islnk" + - "deploy_helper.new_release_path in current_path.stat.lnk_source" + - "not current_path_unfinished_filename.stat.exists" +- stat: path={{ deploy_helper.releases_path }}/third + register: third_release_path +- shell: "ls {{ deploy_helper.releases_path }} | wc -l" + register: releases_count +- name: Assert State=finalize with default parameters (clean=true checks) + assert: + that: + - "not third_release_path.stat.exists" + - "releases_count.stdout|trim == '6'" +- deploy_helper: path={{ deploy_helper_test_root }} release={{ deploy_helper.new_release }} state=query +- name: Assert State=finalize with default parameters (previous_release checks) + assert: + that: + - "deploy_helper.new_release == deploy_helper.previous_release" + +- name: State=absent with default parameters + deploy_helper: path={{ deploy_helper_test_root }} state=absent +- stat: path={{ deploy_helper_test_root }} + register: project_path +- name: Assert State=absent with default parameters + assert: + that: + - "not project_path.stat.exists" + +- debug: msg="Clearing all release data and facts ---------" + +- name: State=present with shared_path set to False + deploy_helper: path={{ deploy_helper_test_root }} state=present shared_path='' +- stat: path={{ deploy_helper.releases_path }} + register: releases_path +- stat: path={{ deploy_helper.shared_path }} + register: shared_path +- name: Assert State=present with shared_path set to False + assert: + that: + - "releases_path.stat.exists" + - "not shared_path.stat.exists" + +# Setup older releases for tests +- file: path={{ deploy_helper.releases_path }}/{{ item }} state=directory + with_items: ['first', 'second', 'third', 'fourth', 'fifth'] +# Setup the new release +- file: path={{ deploy_helper.new_release_path }} state=directory +# Add a buildfile, just like in a real deploy +- copy: content='' dest={{ deploy_helper.new_release_path }}/{{ deploy_helper.unfinished_filename }} +# Add a buildfile, to an older deploy +- copy: content='' dest={{ deploy_helper.releases_path }}/third/{{ deploy_helper.unfinished_filename }} + +- shell: "ls {{ deploy_helper_test_root }}/releases | wc -l" + register: before_releases_count +- name: State=clean with keep_releases=3 + deploy_helper: path={{ deploy_helper_test_root }} release={{ deploy_helper.new_release }} state=clean keep_releases=3 +- stat: path={{ deploy_helper.releases_path }}/third + register: third_release_path +- shell: "ls {{ deploy_helper.releases_path }} | wc -l" + register: releases_count +- name: Assert State=finalize with default parameters (clean=true checks) + assert: + that: + - "not third_release_path.stat.exists" + - "before_releases_count.stdout|trim == '6'" + - "releases_count.stdout|trim == '3'" + +# Remove the root folder +- file: path={{ deploy_helper_test_root }} state=absent diff --git a/ansible_collections/community/general/tests/integration/targets/discord/README.md b/ansible_collections/community/general/tests/integration/targets/discord/README.md new file mode 100644 index 000000000..528ea0643 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/discord/README.md @@ -0,0 +1,20 @@ + + +The integration tests can be executed locally: + +1. Create or use an existing discord server +2. Open `Server Settings` and navigate to `Integrations` tab +3. Click `Create Webhook` to create a new webhook +4. Click `Copy Webhook URL` and extract the webhook_id + webhook_token + + Example: https://discord.com/api/webhooks/`webhook_id`/`webhook_token` + +5. Replace the variables `discord_id` and `discord_token` in the var file +6. Run the integration test +```` +ansible-test integration -v --color yes discord --allow-unsupported +```` diff --git a/ansible_collections/community/general/tests/integration/targets/discord/aliases b/ansible_collections/community/general/tests/integration/targets/discord/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/discord/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/discord/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/discord/defaults/main.yml new file mode 100644 index 000000000..ef01141ca --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/discord/defaults/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 + +discord_id: 000 +discord_token: xxx diff --git a/ansible_collections/community/general/tests/integration/targets/discord/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/discord/tasks/main.yml new file mode 100644 index 000000000..29314ba23 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/discord/tasks/main.yml @@ -0,0 +1,69 @@ +--- +#################################################################### +# 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: Send basic message + community.general.discord: + webhook_id: "{{ discord_id }}" + webhook_token: "{{ discord_token }}" + content: "Messages from ansible-test" + register: result + +- name: Check result + assert: + that: + - result is changed + - result.http_code == 204 + +- name: Send embeds + community.general.discord: + webhook_id: "{{ discord_id }}" + webhook_token: "{{ discord_token }}" + embeds: + - title: "Title of embed message 1" + description: "Description embed message 1" + footer: + text: "author ansible-test" + image: + url: "https://avatars.githubusercontent.com/u/44586252?s=200&v=4" + - title: "Title of embed message 2" + description: "Description embed message 2" + footer: + text: "author ansible-test" + icon_url: "https://avatars.githubusercontent.com/u/44586252?s=200&v=4" + fields: + - name: "Field 1" + value: 1 + - name: "Field 2" + value: "Text" + timestamp: "{{ ansible_date_time.iso8601 }}" + username: Ansible Test + avatar_url: "https://avatars.githubusercontent.com/u/44586252?s=200&v=4" + register: result + +- name: Check result + assert: + that: + - result is changed + - result.http_code == 204 + +- name: Use a wrong token + community.general.discord: + webhook_id: "{{ discord_id }}" + webhook_token: "wrong_token" + content: "Messages from ansible-test" + register: result + ignore_errors: true + +- name: Check result + assert: + that: + - result is not changed + - result.http_code == 401 + - result.response.message == "Invalid Webhook Token" diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/aliases b/ansible_collections/community/general/tests/integration/targets/django_manage/aliases new file mode 100644 index 000000000..98aed9e9d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/aliases @@ -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 + +azp/posix/2 +skip/python2 +skip/freebsd +skip/macos +skip/osx +skip/rhel8.2 +skip/rhel8.3 +skip/rhel8.4 +skip/rhel8.5 +skip/rhel9.0 +skip/rhel9.1 diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/core/settings.py b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/core/settings.py new file mode 100644 index 000000000..881221c06 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/core/settings.py @@ -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 + +# single_app_project/core/settings.py +SECRET_KEY = 'testtesttesttesttest' diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/manage.py b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/manage.py new file mode 100755 index 000000000..4b4eddcb6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/1045-single-app-project/single_app_project/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# 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 + +# single_app_project/manage.py +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'single_app_project.core.settings') + from django.core.management import execute_from_command_line + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py new file mode 100755 index 000000000..be3140f44 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# 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 + +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'p1.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/settings.py b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/settings.py new file mode 100644 index 000000000..86b3ae64c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/settings.py @@ -0,0 +1,133 @@ +# -*- 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 + +""" +Django settings for p1 project. + +Generated by 'django-admin startproj' using Django 3.1.5. + +For more information on this file, see +https://docs.djangoproject.com/en/3.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.1/ref/settings/ +""" + +import os +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '%g@gyhl*q@@g(_ab@t^76dao^#b9-v8mw^50)x_bv6wpl+mukj' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'p1.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'p1.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.1/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = '/tmp/django-static' + +if "DJANGO_ANSIBLE_RAISE" in os.environ: + raise ValueError("DJANGO_ANSIBLE_RAISE={0}".format(os.environ["DJANGO_ANSIBLE_RAISE"])) diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/urls.py b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/urls.py new file mode 100644 index 000000000..36cb59275 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/simple_project/p1/p1/urls.py @@ -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 + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +"""p1 URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path + +urlpatterns = [ + path('admin/', admin.site.urls), +] diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/startproj/.keep b/ansible_collections/community/general/tests/integration/targets/django_manage/files/base_test/startproj/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/django_manage/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/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/django_manage/tasks/main.yaml b/ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml new file mode 100644 index 000000000..c07b53893 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml @@ -0,0 +1,84 @@ +# Test code for django_manage module +# +# Copyright (c) 2020, Alexei Znamensky +# 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 temporary test directory + tempfile: + state: directory + suffix: .django_manage + register: tmp_django_root + +- name: Install virtualenv on CentOS 8 + package: + name: virtualenv + state: present + when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '8' + +- name: Install virtualenv on Arch Linux + pip: + name: virtualenv + state: present + when: ansible_os_family == 'Archlinux' + +- name: Install required library + pip: + name: django + state: present + virtualenv: "{{ tmp_django_root.path }}/venv" + +- name: Copy files + copy: + src: base_test/ + dest: "{{ tmp_django_root.path }}" + mode: preserve + +- name: Create project + command: + chdir: "{{ tmp_django_root.path }}/startproj" + cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startproject test_django_manage_1" + +- name: Create app + command: + chdir: "{{ tmp_django_root.path }}/startproj" + cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startapp app1" + +- name: Check + community.general.django_manage: + project_path: "{{ tmp_django_root.path }}/startproj/test_django_manage_1" + command: check + virtualenv: "{{ tmp_django_root.path }}/venv" + +- name: Check simple_project + community.general.django_manage: + project_path: "{{ tmp_django_root.path }}/simple_project/p1" + command: check + virtualenv: "{{ tmp_django_root.path }}/venv" + +- name: Check custom project + community.general.django_manage: + project_path: "{{ tmp_django_root.path }}/1045-single-app-project/single_app_project" + pythonpath: "{{ tmp_django_root.path }}/1045-single-app-project/" + command: check + virtualenv: "{{ tmp_django_root.path }}/venv" + +- name: Run collectstatic --noinput on simple project + community.general.django_manage: + project_path: "{{ tmp_django_root.path }}/simple_project/p1" + command: collectstatic --noinput + virtualenv: "{{ tmp_django_root.path }}/venv" + +- name: Trigger exception with environment variable + community.general.django_manage: + project_path: "{{ tmp_django_root.path }}/simple_project/p1" + command: collectstatic --noinput + virtualenv: "{{ tmp_django_root.path }}/venv" + environment: + DJANGO_ANSIBLE_RAISE: blah + ignore_errors: true + register: env_raise + +- name: Check env variable reached manage.py + ansible.builtin.assert: + that: + - "'ValueError: DJANGO_ANSIBLE_RAISE=blah' in env_raise.msg" diff --git a/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/aliases b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/aliases new file mode 100644 index 000000000..b85ae6419 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/freebsd +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/install.yml b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/install.yml new file mode 100644 index 000000000..9773d87dc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/install.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: Install dnf versionlock plugin + dnf: + name: dnf-plugin-versionlock + state: present +... diff --git a/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_bash.yml b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_bash.yml new file mode 100644 index 000000000..56357e01c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_bash.yml @@ -0,0 +1,36 @@ +--- +# 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: Clear locklist + community.general.dnf_versionlock: + state: clean + register: clear_locklist + +- assert: + that: + - clear_locklist.locklist_post | length == 0 + +- name: Lock installed package bash + community.general.dnf_versionlock: + name: bash + state: present + register: lock_bash + +- assert: + that: + - lock_bash is changed + - lock_bash.locklist_post | length == 1 + +- name: Unlock installed package bash + community.general.dnf_versionlock: + name: bash + state: absent + register: unlock_bash + +- assert: + that: + - unlock_bash is changed + - unlock_bash.locklist_post | length == 0 +... diff --git a/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_updates.yml b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_updates.yml new file mode 100644 index 000000000..b3fceb26f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/lock_updates.yml @@ -0,0 +1,74 @@ +--- +# 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: Check packages with updates + dnf: + list: updates + register: updates + +- name: Set local facts + set_fact: + _packages: "{{ (updates.results | map(attribute='name') | list)[:5] }}" + +- debug: + msg: + - "The packages to be locked and unlocked are: {{ _packages}}" + +- block: + - name: Clear locklist + community.general.dnf_versionlock: + state: clean + register: clear_locklist + + - assert: + that: + - clear_locklist.locklist_post | length == 0 + + - name: Lock packages with updates + dnf_versionlock: + name: "{{ _packages }}" + state: present + register: lock_packages + + - assert: + that: + - lock_packages is changed + - (lock_packages.locklist_post | length) <= (_packages | length) + + - name: Update packages with updates while locked + command: >- + dnf update -y + --setopt=obsoletes=0 {{ _packages | join(' ') }} + register: update_locked_packages + changed_when: '"Nothing to do" not in update_locked_packages.stdout' + + - assert: + that: + - update_locked_packages is not changed + + - name: Unlock packages with updates + dnf_versionlock: + name: "{{ _packages }}" + state: absent + register: unlock_packages + + - assert: + that: + - unlock_packages is changed + - unlock_packages.locklist_post | length == 0 + + - name: Update packages + dnf: + name: "{{ _packages }}" + state: latest + check_mode: true + register: update_packages + + - assert: + that: + - update_packages is changed + + when: updates.results | length > 0 +... diff --git a/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/main.yml new file mode 100644 index 000000000..51e823ffd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dnf_versionlock/tasks/main.yml @@ -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 + +- block: + - include_tasks: install.yml + - include_tasks: lock_bash.yml + - include_tasks: lock_updates.yml + when: (ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('23', '>=')) or + (ansible_distribution in ['RedHat', 'CentOS'] and ansible_distribution_major_version is version('8', '>=')) +... diff --git a/ansible_collections/community/general/tests/integration/targets/dpkg_divert/aliases b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/aliases new file mode 100644 index 000000000..050bf89b4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/osx +skip/macos +skip/rhel +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/main.yml new file mode 100644 index 000000000..910f174e1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/main.yml @@ -0,0 +1,13 @@ +--- +#################################################################### +# 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: "include tasks for Debian family" + include_tasks: prepare.yml + when: ansible_pkg_mgr == "apt" diff --git a/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/prepare.yml b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/prepare.yml new file mode 100644 index 000000000..94566b41e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/prepare.yml @@ -0,0 +1,43 @@ +--- +# 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: "set variables for the entire playbook" + set_fact: + foobarrc: "{{ foobarrc }}" + foobarrc_ansible: "{{ foobarrc }}.ansible" + foobarrc_distrib: "{{ foobarrc }}.distrib" + foobarrc_oldtext: "# foobar configuration file\n# Please refer to the documentation for details\n" + foobarrc_oldsha1: "e1c54c36d2fd1b8d67d1826e49b95ac8c0f24c0a" + foobarrc_newtext: "# Custom foobar configuration file\nFOO=bar\nBAR=foo" + foobarrc_newsha1: "3fe6c890519fb48e27c1b0e3e37afb11357d5cac" + vars: + foobarrc: "/etc/foobarrc" + +- name: "remove foobarrc diversion" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + become: true + +- name: "remove test files" + file: + path: "{{ dpkg_divert_item }}" + state: absent + loop: + - "{{ foobarrc_ansible }}" + - "{{ foobarrc_distrib }}" + loop_control: + loop_var: dpkg_divert_item + become: true + + +- block: + - name: "include tasks to perform basic tests (create, remove, update)" + include_tasks: tests/01-basic.yml + + - name: "include tasks to perform other tests (rename)" + include_tasks: tests/02-rename.yml + become: true + diff: true diff --git a/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/01-basic.yml b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/01-basic.yml new file mode 100644 index 000000000..78863d1db --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/01-basic.yml @@ -0,0 +1,291 @@ +--- +# 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 + +################################################################################ +# TEST 01: state=present + +- name: "create foobarrc for tests" + copy: + dest: "{{ foobarrc }}" + content: "{{ foobarrc_oldtext }}" + + +- name: "divert foobarrc (check mode, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + state: present + register: diversion_0 + check_mode: true + +- name: "divert foobarrc (must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + state: present + register: diversion_1 + + +- name: "divert foobarrc (must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: present + register: diversion_2 + +- name: "divert foobarrc (check mode, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: present + register: diversion_3 + check_mode: true + + +# Ensure that 'rename' has no effect when state is not changed + +- name: "divert foobarrc (rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: present + rename: true + register: diversion_4 + +- name: "divert foobarrc (check mode, rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: present + rename: true + register: diversion_5 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must still be there)" + stat: + path: "{{ foobarrc }}" + register: diversion_6 + +- name: "stat foobarrc.distrib (must not exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_7 + +- name: "assert that results of test 01 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - diversion_4 is not changed + - diversion_5 is not changed + - diversion_6.stat.exists + - diversion_6.stat.checksum == foobarrc_oldsha1 + - not diversion_7.stat.exists + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_4.diversion == diversion_5.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + - diversion_4.commands == diversion_5.commands + quiet: true + + +################################################################################ +# TEST 02: state=absent + +- name: "remove diversion for foobarrc (check mode, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + register: diversion_0 + check_mode: true + +- name: "remove diversion for foobarrc (must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + register: diversion_1 + + +- name: "remove diversion for foobarrc (must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + register: diversion_2 + +- name: "remove diversion for foobarrc (check mode, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + register: diversion_3 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must still be there)" + stat: + path: "{{ foobarrc }}" + register: diversion_4 + +- name: "stat foobarrc.distrib (must not exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_5 + +- name: "assert that results of test 02 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - diversion_4.stat.exists + - diversion_4.stat.checksum == foobarrc_oldsha1 + - not diversion_5.stat.exists + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + quiet: true + + +################################################################################ +# TEST 03: holder=ansible + +- name: "create foobarrc diversion with defaults" + dpkg_divert: + path: "{{ foobarrc }}" + + +- name: "update foobarrc diversion holder (check mode, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + holder: "ansible" + register: diversion_0 + check_mode: true + +- name: "update foobarrc diversion holder (must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + holder: "ansible" + register: diversion_1 + + +- name: "update foobarrc diversion holder (must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + holder: "ansible" + register: diversion_2 + +- name: "update foobarrc diversion holder (check mode, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + holder: "ansible" + register: diversion_3 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must still be there)" + stat: + path: "{{ foobarrc }}" + register: diversion_4 + +- name: "stat foobarrc.distrib (must not exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_5 + +- name: "assert that results of test 03 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - diversion_4.stat.exists + - diversion_4.stat.checksum == foobarrc_oldsha1 + - not diversion_5.stat.exists + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + quiet: true + +- name: "remove foobarrc diversion" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + + +################################################################################ +# TEST 04: divert=/etc/foobarrc.ansible + +- name: "create foobarrc diversion with defaults" + dpkg_divert: + path: "{{ foobarrc }}" + + +- name: "update foobarrc divert path (check mode, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_0 + check_mode: true + +- name: "update foobarrc divert path (must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_1 + + +- name: "update foobarrc divert path (must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_2 + +- name: "update foobarrc divert path (check mode, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_3 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must still be there)" + stat: + path: "{{ foobarrc }}" + register: diversion_4 + +- name: "stat foobarrc.ansible (must not exist)" + stat: + path: "{{ foobarrc_ansible }}" + register: diversion_5 + +- name: "assert that results of test 04 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - diversion_4.stat.exists + - diversion_4.stat.checksum == foobarrc_oldsha1 + - not diversion_5.stat.exists + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + quiet: true + +- name: "remove foobarrc diversion" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/02-rename.yml b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/02-rename.yml new file mode 100644 index 000000000..6c95a7291 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/dpkg_divert/tasks/tests/02-rename.yml @@ -0,0 +1,384 @@ +--- +# 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 + +################################################################################ +# TEST 05: rename=yes, state=present + +- name: "create diversion for foobarrc (check mode, rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + register: diversion_0 + check_mode: true + +- name: "create diversion for foobarrc (rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + register: diversion_1 + + +- name: "create diversion for foobarrc (rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + register: diversion_2 + +- name: "create diversion for foobarrc (check mode, rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + register: diversion_3 + check_mode: true + + +# Get results + +- name: "stat foobarrc (must not exist)" + stat: + path: "{{ foobarrc }}" + register: diversion_4 + +- name: "stat foobarrc.distrib (must exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_5 + +- name: "assert that results of test 05 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - not diversion_4.stat.exists + - diversion_5.stat.exists + - diversion_5.stat.checksum == foobarrc_oldsha1 + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + quiet: true + + +################################################################################ +# TEST 06: rename=yes, state=absent + +- name: "remove diversion for foobarrc (check mode, rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + state: absent + register: diversion_0 + check_mode: true + +- name: "remove diversion for foobarrc (rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + state: absent + register: diversion_1 + + +- name: "remove diversion for foobarrc (rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + state: absent + register: diversion_2 + +- name: "remove diversion for foobarrc (check mode, rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + state: absent + register: diversion_3 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must exist)" + stat: + path: "{{ foobarrc }}" + register: diversion_4 + +- name: "stat foobarrc.distrib (must not exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_5 + +- name: "assert that results of test 06 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - diversion_4.stat.exists + - diversion_4.stat.checksum == foobarrc_oldsha1 + - not diversion_5.stat.exists + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + quiet: true + + +################################################################################ +# TEST 07: rename=yes, force=yes, state=present + +- name: "create foobarrc.distrib for tests" + copy: + dest: "{{ foobarrc_distrib }}" + content: "{{ foobarrc_oldtext }}" + + +- name: "create diversion for foobarrc (check mode, rename, must fail)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + register: diversion_0 + ignore_errors: true + check_mode: true + +- name: "create diversion for foobarrc (rename, must fail)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + register: diversion_1 + ignore_errors: true + + +- name: "create diversion for foobarrc (check mode, force rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + force: true + register: diversion_2 + check_mode: true + +- name: "create diversion for foobarrc (force rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + force: true + register: diversion_3 + + +- name: "create diversion for foobarrc (force rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + force: true + register: diversion_4 + +- name: "create diversion for foobarrc (check mode, force rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + rename: true + force: true + register: diversion_5 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must not exist)" + stat: + path: "{{ foobarrc }}" + register: diversion_6 + +- name: "stat foobarrc.distrib (must exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_7 + +- name: "assert that results of test 07 are as expected" + assert: + that: + - diversion_0 is failed + - diversion_1 is failed + - diversion_2 is changed + - diversion_3 is changed + - diversion_4 is not changed + - diversion_5 is not changed + - not diversion_6.stat.exists + - diversion_7.stat.exists + - diversion_7.stat.checksum == foobarrc_oldsha1 + - diversion_0 == diversion_1 + - diversion_2.diversion == diversion_3.diversion + - diversion_4.diversion == diversion_5.diversion + - diversion_2.commands == diversion_3.commands + - diversion_4.commands == diversion_5.commands + quiet: true + + +################################################################################ +# TEST 08: state=present, update an existing divert path + +- name: "create foobarrc with new contents for tests" + copy: + dest: "{{ foobarrc }}" + content: "{{ foobarrc_newtext }}" + + +- name: "create diversion for foobarrc (check mode, update divert path, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_0 + check_mode: true + +- name: "create diversion for foobarrc (update divert path, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_1 + + +- name: "create diversion for foobarrc (update divert path, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_2 + +- name: "create diversion for foobarrc (check mode, update divert path, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + divert: "{{ foobarrc_ansible }}" + register: diversion_3 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must exist)" + stat: + path: "{{ foobarrc }}" + register: diversion_4 + +- name: "stat foobarrc.ansible (must exist)" + stat: + path: "{{ foobarrc_ansible }}" + register: diversion_5 + +- name: "stat foobarrc.distrib (must not exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_6 + +- name: "assert that results of test 08 are as expected" + assert: + that: + - diversion_0 is changed + - diversion_1 is changed + - diversion_2 is not changed + - diversion_3 is not changed + - diversion_4.stat.exists + - diversion_4.stat.checksum == foobarrc_newsha1 + - diversion_5.stat.exists + - diversion_5.stat.checksum == foobarrc_oldsha1 + - not diversion_6.stat.exists + - diversion_0.diversion == diversion_1.diversion + - diversion_2.diversion == diversion_3.diversion + - diversion_0.commands == diversion_1.commands + - diversion_2.commands == diversion_3.commands + quiet: true + + +################################################################################ +# TEST 09: rename=yes, force=yes, state=absent + +- name: "remove diversion for foobarrc (check mode, rename, must fail)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + rename: true + register: diversion_0 + ignore_errors: true + check_mode: true + +- name: "remove diversion for foobarrc (rename, must fail)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + rename: true + register: diversion_1 + ignore_errors: true + + +- name: "remove diversion for foobarrc (check mode, force rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + rename: true + force: true + register: diversion_2 + check_mode: true + +- name: "remove diversion for foobarrc (force rename, must report a change)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + rename: true + force: true + register: diversion_3 + + +- name: "remove diversion for foobarrc (force rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + rename: true + force: true + register: diversion_4 + +- name: "remove diversion for foobarrc (check mode, force rename, must NOT report a change, idempotency)" + dpkg_divert: + path: "{{ foobarrc }}" + state: absent + rename: true + force: true + register: diversion_5 + check_mode: true + + +# Check results + +- name: "stat foobarrc (must exist)" + stat: + path: "{{ foobarrc }}" + register: diversion_6 + +- name: "stat foobarrc.distrib (must not exist)" + stat: + path: "{{ foobarrc_distrib }}" + register: diversion_7 + +- name: "stat foobarrc.ansible (must not exist)" + stat: + path: "{{ foobarrc_ansible }}" + register: diversion_8 + +- name: "assert that results of test 09 are as expected" + assert: + that: + - diversion_0 is failed + - diversion_1 is failed + - diversion_2 is changed + - diversion_3 is changed + - diversion_4 is not changed + - diversion_5 is not changed + - diversion_6.stat.exists + - diversion_6.stat.checksum == foobarrc_oldsha1 + - not diversion_7.stat.exists + - not diversion_8.stat.exists + - diversion_0 == diversion_1 + - diversion_2.diversion == diversion_3.diversion + - diversion_4.diversion == diversion_5.diversion + - diversion_2.commands == diversion_3.commands + - diversion_4.commands == diversion_5.commands + quiet: true diff --git a/ansible_collections/community/general/tests/integration/targets/etcd3/aliases b/ansible_collections/community/general/tests/integration/targets/etcd3/aliases new file mode 100644 index 000000000..264446580 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/etcd3/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 +destructive +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/etcd3/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/etcd3/meta/main.yml new file mode 100644 index 000000000..f922f5506 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/etcd3/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_etcd3 diff --git a/ansible_collections/community/general/tests/integration/targets/etcd3/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/etcd3/tasks/main.yml new file mode 100644 index 000000000..2fe7435dc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/etcd3/tasks/main.yml @@ -0,0 +1,18 @@ +--- +#################################################################### +# 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 etcd3 module +# Copyright (c) 2017, Jean-Philippe Evrard +# 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_tests for supported distros + include_tasks: run_tests.yml + when: + - ansible_distribution | lower ~ "-" ~ ansible_distribution_major_version | lower != 'centos-6' diff --git a/ansible_collections/community/general/tests/integration/targets/etcd3/tasks/run_tests.yml b/ansible_collections/community/general/tests/integration/targets/etcd3/tasks/run_tests.yml new file mode 100644 index 000000000..4bd8fa4ec --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/etcd3/tasks/run_tests.yml @@ -0,0 +1,81 @@ +--- +# test code for the etcd3 module +# Copyright (c) 2017, Jean-Philippe Evrard +# Copyright 2020, SCC France, Eric Belhomme +# 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 + +# ============================================================ + +# Integration tests +- name: Check mode, show need change + etcd3: + key: "foo" + value: "bar" + state: "present" + register: _etcd3_prst_chktst + check_mode: true + +- name: Change to new value + etcd3: + key: "foo" + value: "bar" + state: "present" + register: _etcd3_prst_chgtst + +- name: Idempotency test, show unchanged. + etcd3: + key: "foo" + value: "bar" + state: "present" + register: _etcd3_prst_idmptnttst + +- name: Idempotency test in check mode, show unchanged + etcd3: + key: "foo" + value: "bar" + state: "present" + register: _etcd3_prst_idmptntchktst + check_mode: true + +- name: Check mode, show need removal of key + etcd3: + key: "foo" + value: "baz" + state: "absent" + register: _etcd3_absnt_chktst + check_mode: true + +- name: Remove foo key + etcd3: + key: "foo" + value: "baz" + state: "absent" + register: _etcd3_absnt_chgtst + +- name: Idempotency test in check mode, show unchanged + etcd3: + key: "foo" + value: "baz" + state: "absent" + register: _etcd3_absnt_idmptnttst + check_mode: true + +- name: Idempotency test, show unchanged + etcd3: + key: "foo" + value: "baz" + state: "absent" + register: _etcd3_absnt_idmptntchktst + +- name: Checking the status are expected + assert: + that: + - _etcd3_prst_chktst is changed + - _etcd3_prst_chgtst is changed + - _etcd3_prst_idmptnttst is not changed + - _etcd3_prst_idmptntchktst is not changed + - _etcd3_absnt_chktst is changed + - _etcd3_absnt_chgtst is changed + - _etcd3_absnt_idmptnttst is not changed + - _etcd3_absnt_idmptntchktst is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/aliases b/ansible_collections/community/general/tests/integration/targets/filesize/aliases new file mode 100644 index 000000000..7642e70da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/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/1 +azp/posix/vm diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/filesize/defaults/main.yml new file mode 100644 index 000000000..d51108276 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/defaults/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 + +filesize_testdir: "/tmp/testdir" +filesize_testfile: "{{ filesize_testdir }}/testfile" +filesize_testlink: "{{ filesize_testdir }}/testlink" diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/basics.yml b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/basics.yml new file mode 100644 index 000000000..3c0673189 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/basics.yml @@ -0,0 +1,411 @@ +--- +# 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 + +# Test module with basic parameters. +# Create a file, grow it, reduce it to its initial size and check the match +# between initial and final checksums. Also check size formats consistency +# (as 57001B == 57001 B == 57.001 kB, for example, or 0 block or 0 unit is +# zero, etc). + +- name: Create an empty file (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0 + register: filesize_test_basic_01 + check_mode: true + +- name: Stat the file (should not exist) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_01 + + +- name: Create an empty file + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0 + register: filesize_test_basic_02 + +- name: Stat the file (should exist now) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_02 + + +- name: Create an empty file (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0G + register: filesize_test_basic_03 + check_mode: true + +- name: Create an empty file (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0G + register: filesize_test_basic_04 + +- name: Stat the file (should still exist, unchanged) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_04 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + # check_mode & idempotency are in good shape. + - filesize_test_basic_01 is changed + - filesize_test_basic_02 is changed + - filesize_test_basic_03 is not changed + - filesize_test_basic_04 is not changed + + # check_mode returns the same command than actual mode. + - filesize_test_basic_02.cmd == filesize_test_basic_01.cmd + - filesize_test_basic_03.cmd is undefined + - filesize_test_basic_04.cmd is undefined + + # Module's specific return results are consistent with user input, that + # means: with *expected* results. + - filesize_test_basic_01.filesize.bytes == 0 + - filesize_test_basic_02.filesize.bytes == 0 + - filesize_test_basic_03.filesize.bytes == 0 + - filesize_test_basic_04.filesize.bytes == 0 + + - filesize_test_basic_01.size_diff == 0 + - filesize_test_basic_02.size_diff == 0 + - filesize_test_basic_03.size_diff == 0 + - filesize_test_basic_04.size_diff == 0 + + # Results populated by module.set_fs_attributes_if_different() are still + # consistent with current state of the file. + - filesize_test_basic_01.state is undefined + - filesize_test_basic_02.state in ["file"] + - filesize_test_basic_01.size is undefined + - filesize_test_basic_02.size == 0 + - filesize_test_basic_03.size == 0 + - filesize_test_basic_04.size == 0 + + # Cross results with those retrieved by another module. + - not filesize_stat_basic_01.stat.exists + - filesize_stat_basic_02.stat.exists + - filesize_stat_basic_02.stat.isreg + - filesize_stat_basic_02.stat.size == 0 + - filesize_stat_basic_04.stat.size == 0 + + +- name: Fill the file up to 57kB (57000B) with random data (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57kB + source: /dev/urandom + register: filesize_test_basic_11 + check_mode: true + +- name: Stat the file (should still be unchanged) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_11 + + +- name: Fill the file up to 57kB (57000B) with random data + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57kB + source: /dev/urandom + register: filesize_test_basic_12 + +- name: Stat the resulting file (and get its checksum) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_12 + +- name: Store checksum as fact + ansible.builtin.set_fact: + filesize_test_checksum: "{{ filesize_stat_basic_12.stat.checksum }}" + + +- name: Fill the file up to 57000B (57kB) with random data (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57000B + source: /dev/urandom + register: filesize_test_basic_13 + check_mode: true + +- name: Fill the file up to 57000B (57kB) with random data (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57000B + source: /dev/urandom + register: filesize_test_basic_14 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_14 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_basic_11 is changed + - filesize_test_basic_12 is changed + - filesize_test_basic_13 is not changed + - filesize_test_basic_14 is not changed + + - filesize_test_basic_12.cmd == filesize_test_basic_11.cmd + - filesize_test_basic_13.cmd is undefined + - filesize_test_basic_14.cmd is undefined + + - filesize_test_basic_11.filesize.bytes == 57000 + - filesize_test_basic_12.filesize.bytes == 57000 + - filesize_test_basic_13.filesize.bytes == 57000 + - filesize_test_basic_14.filesize.bytes == 57000 + + - filesize_test_basic_11.size_diff == 57000 + - filesize_test_basic_12.size_diff == 57000 + - filesize_test_basic_13.size_diff == 0 + - filesize_test_basic_14.size_diff == 0 + + - filesize_stat_basic_11.stat.size == 0 + - filesize_stat_basic_12.stat.size == 57000 + - filesize_stat_basic_14.stat.size == 57000 + + - filesize_stat_basic_14.stat.checksum == filesize_test_checksum + + + +- name: Expand the file with 1 byte (57001B) (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57001B + register: filesize_test_basic_21 + check_mode: true + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_21 + + +- name: Expand the file with 1 byte (57001B) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57001B + register: filesize_test_basic_22 + +- name: Stat the file (should have grown of 1 byte) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_22 + + +- name: Expand the file with 1 byte (57.001 kB) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57.001 kB + register: filesize_test_basic_23 + check_mode: true + +- name: Expand the file with 1 byte (57.001 kB) (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57.001 kB + register: filesize_test_basic_24 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_24 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_basic_21 is changed + - filesize_test_basic_22 is changed + - filesize_test_basic_23 is not changed + - filesize_test_basic_24 is not changed + + - filesize_test_basic_22.cmd == filesize_test_basic_21.cmd + - filesize_test_basic_23.cmd is undefined + - filesize_test_basic_24.cmd is undefined + + - filesize_test_basic_21.filesize.bytes == 57001 + - filesize_test_basic_22.filesize.bytes == 57001 + - filesize_test_basic_23.filesize.bytes == 57001 + - filesize_test_basic_24.filesize.bytes == 57001 + + - filesize_test_basic_21.size_diff == 1 + - filesize_test_basic_22.size_diff == 1 + - filesize_test_basic_23.size_diff == 0 + - filesize_test_basic_24.size_diff == 0 + + - filesize_stat_basic_21.stat.size == 57000 + - filesize_stat_basic_22.stat.size == 57001 + - filesize_stat_basic_24.stat.size == 57001 + + - filesize_stat_basic_21.stat.checksum == filesize_test_checksum + - filesize_stat_basic_22.stat.checksum != filesize_test_checksum + - filesize_stat_basic_24.stat.checksum != filesize_test_checksum + + + +- name: Expand the file up to 2 MiB (2*1024*1024 bytes) (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 2 MiB + register: filesize_test_basic_31 + check_mode: true + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_31 + + +- name: Expand the file up to 2 MiB (2*1024*1024 bytes) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 2 MiB + register: filesize_test_basic_32 + +- name: Stat the file again (should have grown to 2MiB) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_32 + + +- name: Expand the file up to 2×1M (2*1024*1024 bytes) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 2 + blocksize: 1M + register: filesize_test_basic_33 + check_mode: true + +- name: Expand the file up to 2×1M (2*1024*1024 bytes) (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 2 + blocksize: 1M + register: filesize_test_basic_34 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_34 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_basic_31 is changed + - filesize_test_basic_32 is changed + - filesize_test_basic_33 is not changed + - filesize_test_basic_34 is not changed + + - filesize_test_basic_32.cmd == filesize_test_basic_31.cmd + - filesize_test_basic_33.cmd is undefined + - filesize_test_basic_34.cmd is undefined + + - filesize_test_basic_31.filesize.bytes == 2*1024**2 + - filesize_test_basic_32.filesize.bytes == 2*1024**2 + - filesize_test_basic_33.filesize.bytes == 2*1024**2 + - filesize_test_basic_34.filesize.bytes == 2*1024**2 + + - filesize_test_basic_31.size_diff == 2*1024**2 - 57001 + - filesize_test_basic_32.size_diff == 2*1024**2 - 57001 + - filesize_test_basic_33.size_diff == 0 + - filesize_test_basic_34.size_diff == 0 + + - filesize_stat_basic_31.stat.size == 57001 + - filesize_stat_basic_32.stat.size == 2*1024**2 + - filesize_stat_basic_34.stat.size == 2*1024**2 + + + +- name: Truncate the file to 57kB (57000B) (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57kB + register: filesize_test_basic_41 + check_mode: true + +- name: Stat the resulting file (should be unchanged) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_41 + + +- name: Truncate the file to 57kB (57000B) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57kB + register: filesize_test_basic_42 + +- name: Stat the resulting file (and get its checksum) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_42 + + +- name: Truncate the file to 57000 B (57kB) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57000 B + register: filesize_test_basic_43 + check_mode: true + +- name: Truncate the file to 57000 B (57kB) (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 57000 B + register: filesize_test_basic_44 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_basic_44 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_basic_41 is changed + - filesize_test_basic_42 is changed + - filesize_test_basic_43 is not changed + - filesize_test_basic_44 is not changed + + - filesize_test_basic_42.cmd == filesize_test_basic_41.cmd + - filesize_test_basic_43.cmd is undefined + - filesize_test_basic_44.cmd is undefined + + - filesize_test_basic_41.filesize.bytes == 57000 + - filesize_test_basic_42.filesize.bytes == 57000 + - filesize_test_basic_43.filesize.bytes == 57000 + - filesize_test_basic_44.filesize.bytes == 57000 + + - filesize_test_basic_41.size_diff == 57000 - 2*1024**2 + - filesize_test_basic_42.size_diff == 57000 - 2*1024**2 + - filesize_test_basic_43.size_diff == 0 + - filesize_test_basic_44.size_diff == 0 + + - filesize_stat_basic_41.stat.size == 2*1024**2 + - filesize_stat_basic_42.stat.size == 57000 + - filesize_stat_basic_44.stat.size == 57000 + + # The original random file is back. + - filesize_stat_basic_41.stat.checksum != filesize_test_checksum + - filesize_stat_basic_42.stat.checksum == filesize_test_checksum + - filesize_stat_basic_44.stat.checksum == filesize_test_checksum + + + +- name: Remove test file + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/errors.yml b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/errors.yml new file mode 100644 index 000000000..351a90ac6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/errors.yml @@ -0,0 +1,133 @@ +--- +# 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 + +# Check error handling of the module. +# 1. Missing or unknown parameters +# 2. Wrong values (missing source device, invalid size...) + +- name: Trigger an error due to missing parameter (path) + community.general.filesize: + size: 1kB + register: filesize_test_error_01 + ignore_errors: true + + +- name: Trigger an error due to missing parameter (size) + community.general.filesize: + path: "{{ filesize_testfile }}" + register: filesize_test_error_02 + ignore_errors: true + + +- name: Trigger an error due to conflicting parameters (force|sparse) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 1MB + force: true + sparse: true + register: filesize_test_error_03 + ignore_errors: true + + +- name: Trigger an error due to invalid file path (not a file) + community.general.filesize: + path: "{{ filesize_testdir }}" + size: 4096B + register: filesize_test_error_04 + ignore_errors: true + + +- name: Trigger an error due to invalid file path (unexisting parent dir) + community.general.filesize: + path: "/unexistent/{{ filesize_testfile }}" + size: 4096B + register: filesize_test_error_05 + ignore_errors: true + + +- name: Trigger an error due to invalid size unit (b)" + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4096b + register: filesize_test_error_06 + ignore_errors: true + + +- name: Trigger an error due to invalid size value (bytes require integer) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 1000.5B + register: filesize_test_error_07 + ignore_errors: true + + +- name: Trigger an error due to invalid blocksize value (not an integer) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 1M + blocksize: "12.5" + register: filesize_test_error_08 + ignore_errors: true + + +- name: Trigger an error due to invalid blocksize value type (dict) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 1M + blocksize: + bytes: 512 + register: filesize_test_error_09 + ignore_errors: true + + +- name: Trigger an error due to invalid source device (/dev/unexistent) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 1M + source: /dev/unexistent + register: filesize_test_error_10 + ignore_errors: true + + +- name: Trigger an error due to invalid source device (/dev/null) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 1M + source: /dev/null + register: filesize_test_error_11 + ignore_errors: true + + +- name: Assert that expected errors have been triggered + ansible.builtin.assert: + that: + - "filesize_test_error_01 is failed" + - "filesize_test_error_01.msg == 'missing required arguments: path'" + - "filesize_test_error_02 is failed" + - "filesize_test_error_02.msg == 'missing required arguments: size'" + - "filesize_test_error_03 is failed" + - "filesize_test_error_03.msg == 'parameters values are mutually exclusive: force=true|sparse=true'" + - "filesize_test_error_04 is failed" + - "filesize_test_error_04.msg == '%s exists but is not a regular file' % filesize_testdir" + - "filesize_test_error_05 is failed" + - "filesize_test_error_05.msg == 'parent directory of the file must exist prior to run this module'" + - "filesize_test_error_06 is failed" + - "filesize_test_error_06.msg is match('invalid size unit')" + - "filesize_test_error_07 is failed" + - "filesize_test_error_07.msg == 'byte is the smallest unit and requires an integer value'" + - "filesize_test_error_08 is failed" + - "filesize_test_error_08.msg == 'invalid blocksize value: bytes require an integer value'" + - "filesize_test_error_09 is failed" + - "filesize_test_error_09.msg is match('invalid value type')" + - "filesize_test_error_10 is failed" + - "filesize_test_error_10.msg == 'dd error while creating file %s with size 1M from source /dev/unexistent: see stderr for details' % filesize_testfile" + - "filesize_test_error_11 is failed" + - "filesize_test_error_11.msg == 'module error while creating file %s with size 1M from source /dev/null: file is 0 bytes long' % filesize_testfile" + + +- name: Remove test file + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/floats.yml b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/floats.yml new file mode 100644 index 000000000..6d1bde22c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/floats.yml @@ -0,0 +1,249 @@ +--- +# 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 + +# Test module with floating point numbers (ensure they're not rounded too +# wrongly), since in python floats are tricky: +# 256.256 * 1000 == 256255.9999999997 +# 512.512 * 1000 == 512511.9999999994 +# 512.513 * 1000 == 512513.0000000006 != .512513 * 1000000 + +- name: Create a file with a size of 512.512kB (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 512.512kB + register: filesize_test_float_01 + check_mode: true + +- name: Stat the file (should not exist) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_01 + + +- name: Create a file with a size of 512.512kB + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 512.512kB + register: filesize_test_float_02 + +- name: Stat the file (should exist now) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_02 + + +- name: Create a file with a size of 0.512512MB (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0.512512MB + register: filesize_test_float_03 + check_mode: true + +- name: Create a file with a size of 0.512512MB (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0.512512MB + register: filesize_test_float_04 + +- name: Stat the file (should still exist, unchanged) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_04 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_float_01 is changed + - filesize_test_float_02 is changed + - filesize_test_float_03 is not changed + - filesize_test_float_04 is not changed + + - filesize_test_float_02.cmd == filesize_test_float_01.cmd + - filesize_test_float_03.cmd is undefined + - filesize_test_float_04.cmd is undefined + + - filesize_test_float_01.filesize.bytes == 512512 + - filesize_test_float_02.filesize.bytes == 512512 + - filesize_test_float_03.filesize.bytes == 512512 + - filesize_test_float_04.filesize.bytes == 512512 + + - filesize_test_float_01.size_diff == 512512 + - filesize_test_float_02.size_diff == 512512 + - filesize_test_float_03.size_diff == 0 + - filesize_test_float_04.size_diff == 0 + + - filesize_test_float_01.state is undefined + - filesize_test_float_02.state in ["file"] + - filesize_test_float_01.size is undefined + - filesize_test_float_02.size == 512512 + - filesize_test_float_03.size == 512512 + - filesize_test_float_04.size == 512512 + + - not filesize_stat_float_01.stat.exists + - filesize_stat_float_02.stat.exists + - filesize_stat_float_02.stat.isreg + - filesize_stat_float_02.stat.size == 512512 + - filesize_stat_float_04.stat.size == 512512 + + + +- name: Create a file with a size of 512.513kB (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 512.513kB + register: filesize_test_float_11 + check_mode: true + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_11 + + +- name: Create a file with a size of 512.513kB + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 512.513kB + register: filesize_test_float_12 + +- name: Stat the file (should have grown of 1 byte) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_12 + + +- name: Create a file with a size of 0.512513MB (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0.512513MB + register: filesize_test_float_13 + check_mode: true + +- name: Create a file with a size of 0.512513MB (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 0.512513MB + register: filesize_test_float_14 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_14 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_float_11 is changed + - filesize_test_float_12 is changed + - filesize_test_float_13 is not changed + - filesize_test_float_14 is not changed + + - filesize_test_float_12.cmd == filesize_test_float_11.cmd + - filesize_test_float_13.cmd is undefined + - filesize_test_float_14.cmd is undefined + + - filesize_test_float_11.filesize.bytes == 512513 + - filesize_test_float_12.filesize.bytes == 512513 + - filesize_test_float_13.filesize.bytes == 512513 + - filesize_test_float_14.filesize.bytes == 512513 + + - filesize_test_float_11.size_diff == 1 + - filesize_test_float_12.size_diff == 1 + - filesize_test_float_13.size_diff == 0 + - filesize_test_float_14.size_diff == 0 + + - filesize_test_float_11.size == 512512 + - filesize_test_float_12.size == 512513 + - filesize_test_float_13.size == 512513 + - filesize_test_float_14.size == 512513 + + - filesize_stat_float_11.stat.size == 512512 + - filesize_stat_float_12.stat.size == 512513 + - filesize_stat_float_14.stat.size == 512513 + + + +- name: Create a file with a size of 4.004MB (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4.004MB + register: filesize_test_float_21 + check_mode: true + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_21 + + +- name: Create a file with a size of 4.004MB + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4.004MB + register: filesize_test_float_22 + +- name: Stat the file (should have grown to 4.004MB) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_22 + + +- name: Create a file with a size of 4.004MB (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4.004MB + register: filesize_test_float_23 + check_mode: true + +- name: Create a file with a size of 4.004MB (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4.004MB + register: filesize_test_float_24 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + register: filesize_stat_float_24 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_float_21 is changed + - filesize_test_float_22 is changed + - filesize_test_float_23 is not changed + - filesize_test_float_24 is not changed + + - filesize_test_float_22.cmd == filesize_test_float_21.cmd + - filesize_test_float_23.cmd is undefined + - filesize_test_float_24.cmd is undefined + + - filesize_test_float_21.filesize.bytes == 4004000 + - filesize_test_float_22.filesize.bytes == 4004000 + - filesize_test_float_23.filesize.bytes == 4004000 + - filesize_test_float_24.filesize.bytes == 4004000 + + - filesize_test_float_21.size_diff == 4004000 - 512513 + - filesize_test_float_22.size_diff == 4004000 - 512513 + - filesize_test_float_23.size_diff == 0 + - filesize_test_float_24.size_diff == 0 + + - filesize_test_float_21.size == 512513 + - filesize_test_float_22.size == 4004000 + - filesize_test_float_23.size == 4004000 + - filesize_test_float_24.size == 4004000 + + - filesize_stat_float_21.stat.size == 512513 + - filesize_stat_float_22.stat.size == 4004000 + - filesize_stat_float_24.stat.size == 4004000 + + +- name: Remove test file + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/main.yml new file mode 100644 index 000000000..68cd8934c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/main.yml @@ -0,0 +1,44 @@ +--- +#################################################################### +# 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: Ensure the test dir is present + ansible.builtin.file: + path: "{{ filesize_testdir }}" + state: directory + +- name: Ensure the test file is absent + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent + +- name: Run all tests and remove the workspace anyway + block: + - name: Include tasks to test error handling + include_tasks: errors.yml + + - name: Include tasks to test basic behaviours + include_tasks: basics.yml + + - name: Include tasks to test playing with floating point numbers + include_tasks: floats.yml + + - name: Include tasks to test playing with sparse files + include_tasks: sparse.yml + when: + - not (ansible_os_family == 'Darwin' and ansible_distribution_version is version('11', '<')) + + - name: Include tasks to test playing with symlinks + include_tasks: symlinks.yml + + always: + - name: Remove test dir + ansible.builtin.file: + path: "{{ filesize_testdir }}" + state: absent 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 new file mode 100644 index 000000000..79145b6e2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml @@ -0,0 +1,286 @@ +--- +# 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 + +# Test module with sparse files + +- name: Create a huge sparse file of 4TB (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4TB + sparse: true + register: filesize_test_sparse_01 + check_mode: true + +- name: Stat the file (should not exist) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_01 + + +- name: Create a huge sparse file of 4TB + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4TB + sparse: true + register: filesize_test_sparse_02 + +- name: Stat the resulting file (should exist now) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_02 + + +- name: Create a huge sparse file of 4TB (4000GB) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4000GB + sparse: true + register: filesize_test_sparse_03 + check_mode: true + +- name: Create a huge sparse file of 4TB (4000GB) (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4000GB + sparse: true + register: filesize_test_sparse_04 + +- name: Create a huge sparse file of 4TB (4000000 × 1MB) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4000000 + blocksize: 1MB + sparse: true + register: filesize_test_sparse_05 + check_mode: true + +- name: Create a huge sparse file of 4TB (4000000 × 1MB) (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4000000 + blocksize: 1MB + sparse: true + register: filesize_test_sparse_06 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_06 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_sparse_01 is changed + - filesize_test_sparse_02 is changed + - filesize_test_sparse_03 is not changed + - filesize_test_sparse_04 is not changed + - filesize_test_sparse_05 is not changed + - filesize_test_sparse_06 is not changed + + - filesize_test_sparse_02.cmd == filesize_test_sparse_01.cmd + - filesize_test_sparse_03.cmd is undefined + - filesize_test_sparse_04.cmd is undefined + - 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.size_diff == 4*1000**4 + - filesize_test_sparse_02.size_diff == 4*1000**4 + - filesize_test_sparse_03.size_diff == 0 + - filesize_test_sparse_04.size_diff == 0 + - filesize_test_sparse_05.size_diff == 0 + - filesize_test_sparse_06.size_diff == 0 + + - 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 + + - 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 + + + +- name: Change sparse file size to 4TiB (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4TiB + sparse: true + register: filesize_test_sparse_11 + check_mode: true + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_11 + + +- name: Change sparse file size to 4TiB + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4TiB + sparse: true + register: filesize_test_sparse_12 + +- name: Stat the file again (should have grown) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_12 + + +- name: Change sparse file size to 4TiB (4096GiB) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4096GiB + sparse: true + register: filesize_test_sparse_13 + check_mode: true + +- name: Change sparse file size to 4TiB (4096GiB) (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4096GiB + sparse: true + register: filesize_test_sparse_14 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_14 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_sparse_11 is changed + - filesize_test_sparse_12 is changed + - filesize_test_sparse_13 is not changed + - filesize_test_sparse_14 is not changed + + - filesize_test_sparse_12.cmd == filesize_test_sparse_11.cmd + - 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_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_stat_sparse_11.stat.size == 4000000000000 + - filesize_stat_sparse_12.stat.size == 4398046511104 + - filesize_stat_sparse_14.stat.size == 4398046511104 + + + +- name: Change sparse file size to 4.321TB (check mode) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4.321TB + sparse: true + register: filesize_test_sparse_21 + check_mode: true + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_21 + + +- name: Change sparse file size to 4.321TB + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4.321TB + sparse: true + register: filesize_test_sparse_22 + +- name: Stat the file again (should have been reduced) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_22 + + +- name: Change sparse file size to 4321×1GB (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4321 + blocksize: 1GB + sparse: true + register: filesize_test_sparse_23 + check_mode: true + +- name: Change sparse file size to 4321×1GB (idempotency) + community.general.filesize: + path: "{{ filesize_testfile }}" + size: 4321 + blocksize: 1GB + sparse: true + register: filesize_test_sparse_24 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_testfile }}" + get_checksum: false + register: filesize_stat_sparse_24 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_sparse_21 is changed + - filesize_test_sparse_22 is changed + - filesize_test_sparse_23 is not changed + - filesize_test_sparse_24 is not changed + + - filesize_test_sparse_22.cmd == filesize_test_sparse_21.cmd + - 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_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_stat_sparse_21.stat.size == 4398046511104 + - filesize_stat_sparse_22.stat.size == 4321000000000 + - filesize_stat_sparse_24.stat.size == 4321000000000 + + + +- name: Remove test file + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/symlinks.yml b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/symlinks.yml new file mode 100644 index 000000000..011889656 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/symlinks.yml @@ -0,0 +1,97 @@ +--- +# 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 + +# Check that the module works with symlinks, as expected, i.e. as dd does: +# follow symlinks. + +- name: Ensure the test file is absent + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent + +- name: Create a broken symlink in the same directory + ansible.builtin.file: + src: "{{ filesize_testfile | basename }}" + dest: "{{ filesize_testlink }}" + state: link + force: true + follow: false + + + +- name: Create a file with a size of 512 kB (512000 bytes) (check mode) + community.general.filesize: + path: "{{ filesize_testlink }}" + size: "512 kB" + register: filesize_test_symlink_01 + check_mode: true + +- name: Create a file with a size of 512 kB (512000 bytes) + community.general.filesize: + path: "{{ filesize_testlink }}" + size: "512 kB" + register: filesize_test_symlink_02 + +- name: Stat the resulting file (not the symlink) + ansible.builtin.stat: + path: "{{ filesize_test_symlink_02.path }}" + register: filesize_stat_symlink_02 + + +- name: Create a file with a size of 500 KiB (512000 bytes) (check mode, idempotency) + community.general.filesize: + path: "{{ filesize_testlink }}" + size: "500 KiB" + register: filesize_test_symlink_03 + check_mode: true + +- name: Create a file with a size of 500 KiB (512000 bytes) (idempotency) + community.general.filesize: + path: "{{ filesize_testlink }}" + size: "500 KiB" + register: filesize_test_symlink_04 + +- name: Stat the file again (should remain the same) + ansible.builtin.stat: + path: "{{ filesize_test_symlink_04.path }}" + register: filesize_stat_symlink_04 + + +- name: Assert that results are as expected + ansible.builtin.assert: + that: + - filesize_test_symlink_01 is changed + - filesize_test_symlink_02 is changed + - filesize_test_symlink_03 is not changed + - filesize_test_symlink_04 is not changed + + - filesize_test_symlink_02.cmd == filesize_test_symlink_01.cmd + - filesize_test_symlink_03.cmd is undefined + - filesize_test_symlink_04.cmd is undefined + + - filesize_test_symlink_01.state is undefined + - filesize_test_symlink_02.state in ["file"] + - filesize_test_symlink_01.size is undefined + - filesize_test_symlink_02.size == 512000 + - filesize_test_symlink_03.size == 512000 + - filesize_test_symlink_04.size == 512000 + + - filesize_stat_symlink_02.stat.size == 512000 + - filesize_stat_symlink_04.stat.size == 512000 + + - filesize_test_symlink_04.path == filesize_test_symlink_02.path + - filesize_test_symlink_04.path != filesize_testlink + + + +- name: Remove test file + ansible.builtin.file: + path: "{{ filesize_testfile }}" + state: absent + +- name: Remove test link + ansible.builtin.file: + path: "{{ filesize_testlink }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/aliases b/ansible_collections/community/general/tests/integration/targets/filesystem/aliases new file mode 100644 index 000000000..a666f7a14 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/aliases @@ -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 + +azp/posix/1 +azp/posix/vm +destructive +skip/aix +skip/osx +skip/macos 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 new file mode 100644 index 000000000..0448d8602 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml @@ -0,0 +1,35 @@ +--- +# 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 + +tested_filesystems: + # key: fstype + # fssize: size (Mo) + # grow: true if resizefs is supported + # Other minimal sizes: + # - 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.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} + + +get_uuid_any: "blkid -c /dev/null -o value -s UUID {{ dev }}" +get_uuid_ufs: "dumpfs {{ dev }} | awk -v sb=superblock -v id=id '$1 == sb && $4 == id {print $6$7}'" +get_uuid_cmd: "{{ get_uuid_ufs if fstype == 'ufs' else get_uuid_any }}" diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/meta/main.yml new file mode 100644 index 000000000..d3facee4f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/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_outside_tmp diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_device.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_device.yml new file mode 100644 index 000000000..8966ec2e6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_device.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 a "disk" file' + community.general.filesize: + path: '{{ image_file }}' + size: '{{ fssize }}M' + force: true + +- vars: + dev: '{{ image_file }}' + block: + - when: fstype == 'lvm' + block: + - name: 'Show next free loop device' + ansible.builtin.command: + cmd: 'losetup -f' + register: loop_device_cmd + + - name: 'Create a loop device for LVM' + ansible.builtin.command: + cmd: 'losetup -f {{ dev }}' + + - name: 'Switch to loop device target for further tasks' + ansible.builtin.set_fact: + dev: "{{ loop_device_cmd.stdout }}" + + - when: fstype == 'ufs' + block: + - name: 'Create a memory disk for UFS' + ansible.builtin.command: + cmd: 'mdconfig -a -f {{ dev }}' + register: memory_disk_cmd + + - name: 'Switch to memory disk target for further tasks' + ansible.builtin.set_fact: + dev: "/dev/{{ memory_disk_cmd.stdout }}" + + - include_tasks: '{{ action }}.yml' + + always: + - name: 'Detach loop device used for LVM' + ansible.builtin.command: + cmd: 'losetup -d {{ dev }}' + removes: '{{ dev }}' + when: fstype == 'lvm' + + - name: 'Detach memory disk used for UFS' + ansible.builtin.command: + cmd: 'mdconfig -d -u {{ dev }}' + removes: '{{ dev }}' + when: fstype == 'ufs' + + - name: 'Clean correct device for LVM and UFS' + ansible.builtin.set_fact: + dev: '{{ image_file }}' + when: fstype in ['lvm', 'ufs'] + + - name: 'Remove disk image file' + ansible.builtin.file: + name: '{{ image_file }}' + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_fs.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_fs.yml new file mode 100644 index 000000000..d5470fa56 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/create_fs.yml @@ -0,0 +1,119 @@ +--- +# 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 filesystem ({{ fstype }})" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + register: fs_result + +- name: "Assert that results are as expected" + ansible.builtin.assert: + that: + - 'fs_result is changed' + - 'fs_result is success' + +- name: "Get UUID of created filesystem" + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: uuid + +- name: "Check that filesystem isn't created if force isn't used" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + register: fs2_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 not changed" + ansible.builtin.assert: + that: + - 'fs2_result is not changed' + - 'fs2_result is success' + - 'uuid.stdout == uuid2.stdout' + +- name: "Check that filesystem is recreated if force is used" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + force: true + register: fs3_result + +- name: "Get UUID of the new filesystem" + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: uuid3 + +- name: "Assert that filesystem UUID is changed" + # libblkid gets no UUID at all for this fstype on FreeBSD + when: not (ansible_system == 'FreeBSD' and fstype == 'reiserfs') + ansible.builtin.assert: + that: + - 'fs3_result is changed' + - 'fs3_result is success' + - 'uuid.stdout != uuid3.stdout' + + +- when: 'grow|bool and (fstype != "vfat" or resize_vfat)' + block: + - name: "Increase fake device" + community.general.filesize: + path: '{{ image_file }}' + size: '{{ fssize | int + 1 }}M' + + - name: "Resize loop device for LVM" + ansible.builtin.command: + cmd: 'losetup -c {{ dev }}' + when: fstype == 'lvm' + + - name: "Resize memory disk for UFS" + ansible.builtin.command: + cmd: 'mdconfig -r -u {{ dev }} -s {{ fssize | int + 1 }}M' + when: fstype == 'ufs' + + - name: "Expand filesystem" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + resizefs: true + register: fs4_result + + - name: "Get UUID of the filesystem" + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: uuid4 + + - name: "Assert that filesystem UUID is not changed" + ansible.builtin.assert: + that: + - 'fs4_result is changed' + - 'fs4_result is success' + - 'uuid3.stdout == uuid4.stdout' # unchanged + +- 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: "Check that resizefs does nothing if device size is not changed" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + resizefs: true + register: fs5_result + + - name: "Assert that the state did not change" + ansible.builtin.assert: + that: + - 'fs5_result is not changed' + - 'fs5_result is succeeded' diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/freebsd_setup.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/freebsd_setup.yml new file mode 100644 index 000000000..03fef66e6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/freebsd_setup.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: "Uninstall e2fsprogs" + ansible.builtin.package: + name: e2fsprogs + state: absent + +- name: "Install util-linux" + ansible.builtin.package: + name: util-linux + state: present 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 new file mode 100644 index 000000000..0ff0f2309 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml @@ -0,0 +1,107 @@ +--- +#################################################################### +# 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 + +- ansible.builtin.debug: + msg: '{{ role_name }}' +- ansible.builtin.debug: + msg: '{{ role_path|basename }}' +- import_tasks: setup.yml + +- include_vars: "{{ lookup('first_found', search) }}" + vars: + search: + files: + - '{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml' + - 'default.yml' + paths: + - '../vars/' + +- include_tasks: create_device.yml + vars: + image_file: '{{ remote_tmp_dir }}/img' + fstype: '{{ item.0.key }}' + fssize: '{{ item.0.value.fssize }}' + grow: '{{ item.0.value.grow }}' + action: '{{ item.1 }}' + when: + # FreeBSD limited support + # Not available: btrfs, lvm, f2fs, ocfs2 + # All BSD systems use swap fs, but only Linux needs mkswap + # Supported: ext2/3/4 (e2fsprogs), xfs (xfsprogs), reiserfs (progsreiserfs), vfat + - 'not (ansible_system == "FreeBSD" and item.0.key in ["btrfs", "f2fs", "swap", "lvm", "ocfs2"])' + # Available on FreeBSD but not on testbed (util-linux conflicts with e2fsprogs): wipefs, mkfs.minix + - 'not (ansible_system == "FreeBSD" and item.1 in ["overwrite_another_fs", "remove_fs"])' + + # Linux limited support + # Not available: ufs (this is FreeBSD's native fs) + - 'not (ansible_system == "Linux" and item.0.key == "ufs")' + + # Other limitations and corner cases + + # f2fs-tools and reiserfs-utils packages not available with RHEL/CentOS on CI + - 'not (ansible_distribution in ["CentOS", "RedHat"] and item.0.key in ["f2fs", "reiserfs"])' + - 'not (ansible_os_family == "RedHat" and ansible_distribution_major_version is version("8", ">=") and + item.0.key == "btrfs")' + # reiserfs-utils package not available with Fedora 35 on CI + - 'not (ansible_distribution == "Fedora" and (ansible_facts.distribution_major_version | int >= 35) and + item.0.key == "reiserfs")' + # reiserfs packages apparently not available with Alpine + - 'not (ansible_distribution == "Alpine" and item.0.key == "reiserfs")' + # ocfs2 only available on Debian based distributions + - 'not (item.0.key == "ocfs2" and ansible_os_family != "Debian")' + # Tests use losetup which can not be used inside unprivileged container + - 'not (item.0.key == "lvm" and ansible_virtualization_type in ["docker", "container", "containerd"])' + # vfat resizing fails on Debian (but not Ubuntu) + - 'not (item.0.key == "vfat" and ansible_distribution == "Debian")' # TODO: figure out why it fails, fix it! + # vfat resizing fails on ArchLinux + - 'not (item.0.key == "vfat" and ansible_distribution == "Archlinux")' # TODO: figure out why it fails, fix it! + # vfat resizing fails on Ubuntu 22.04 + - 'not (item.0.key == "vfat" and ansible_distribution == "Ubuntu" and (ansible_facts.distribution_major_version | int == 22))' + # TODO: figure out why it fails, fix it! + # btrfs-progs cannot be installed on ArchLinux + - 'not (item.0.key == "btrfs" and ansible_distribution == "Archlinux")' # TODO: figure out why it fails, fix it! + + # On CentOS 6 shippable containers, wipefs seems unable to remove vfat signatures + - 'not (ansible_distribution == "CentOS" and ansible_distribution_version is version("7.0", "<") and + item.1 == "remove_fs" and item.0.key == "vfat")' + # On same systems, mkfs.minix (unhandled by the module) can't find the device/file + - 'not (ansible_distribution == "CentOS" and ansible_distribution_version is version("7.0", "<") and + item.1 == "overwrite_another_fs")' + + # The xfsprogs package on newer versions of OpenSUSE (15+) require Python 3, we skip this on our Python 2 container + # OpenSUSE 42.3 Python2 and the other py3 containers are not affected so we will continue to run that + - 'not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and + item.0.key == "xfs" and ansible_python.version.major == 2)' + + # 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 }}" + + +# With FreeBSD extended support (util-linux is not available before 12.2) + +- include_tasks: freebsd_setup.yml + when: + - 'ansible_system == "FreeBSD"' + - 'ansible_distribution_version is version("12.2", ">=")' + +- include_tasks: create_device.yml + vars: + image_file: '{{ remote_tmp_dir }}/img' + fstype: '{{ item.0.key }}' + fssize: '{{ item.0.value.fssize }}' + grow: '{{ item.0.value.grow }}' + action: '{{ item.1 }}' + when: + - 'ansible_system == "FreeBSD"' + - 'ansible_distribution_version is version("12.2", ">=")' + - 'item.0.key in ["xfs", "vfat"]' + loop: "{{ query('dict', tested_filesystems)|product(['create_fs', 'overwrite_another_fs', 'remove_fs'])|list }}" diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/overwrite_another_fs.yml new file mode 100644 index 000000000..69418b22f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/overwrite_another_fs.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 + +- name: 'Recreate "disk" file' + community.general.filesize: + path: '{{ image_file }}' + size: '{{ fssize }}M' + force: true + +- name: 'Create a minix filesystem' + ansible.builtin.command: + cmd: 'mkfs.minix {{ dev }}' + +- name: 'Get UUID of the new filesystem' + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: uuid + +- name: "Check that an existing filesystem (not handled by this module) isn't overwritten when force isn't used" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + register: fs_result + ignore_errors: true + +- name: 'Get UUID of the filesystem' + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: uuid2 + +- name: 'Assert that module failed and filesystem UUID is not changed' + ansible.builtin.assert: + that: + - 'fs_result is failed' + - 'uuid.stdout == uuid2.stdout' + +- name: "Check that an existing filesystem (not handled by this module) is overwritten when force is used" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + force: true + register: fs_result2 + +- name: 'Get UUID of the new filesystem' + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: uuid3 + +- name: 'Assert that module succeeded and filesystem UUID is changed' + ansible.builtin.assert: + that: + - 'fs_result2 is success' + - 'fs_result2 is changed' + - 'uuid2.stdout != uuid3.stdout' diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/remove_fs.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/remove_fs.yml new file mode 100644 index 000000000..c5428b309 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/remove_fs.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 + +# We assume 'create_fs' tests have passed. + +- name: "Create filesystem" + community.general.filesystem: + dev: '{{ dev }}' + fstype: '{{ fstype }}' + +- name: "Get filesystem UUID with 'blkid'" + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: blkid_ref + +- name: "Assert that a filesystem exists on top of the device" + ansible.builtin.assert: + that: + - blkid_ref.stdout | length > 0 + + +# Test check_mode first +- name: "Remove filesystem (check mode)" + community.general.filesystem: + dev: '{{ dev }}' + state: absent + register: wipefs + check_mode: true + +- name: "Get filesystem UUID with 'blkid' (should remain the same)" + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + register: blkid + +- name: "Assert that the state changed but the filesystem still exists" + ansible.builtin.assert: + that: + - wipefs is changed + - blkid.stdout == blkid_ref.stdout + +# Do it +- name: "Remove filesystem" + community.general.filesystem: + dev: '{{ dev }}' + state: absent + register: wipefs + +- name: "Get filesystem UUID with 'blkid' (should be empty)" + ansible.builtin.shell: + cmd: "{{ get_uuid_cmd }}" + changed_when: false + failed_when: false + register: blkid + +- name: "Assert that the state changed and the device has no filesystem" + ansible.builtin.assert: + that: + - wipefs is changed + - blkid.stdout | length == 0 + - blkid.rc == 2 + +# Do it again +- name: "Remove filesystem (idempotency)" + community.general.filesystem: + dev: '{{ dev }}' + state: absent + register: wipefs + +- name: "Assert that the state did not change" + ansible.builtin.assert: + that: + - wipefs is not changed + +# and again +- name: "Remove filesystem (idempotency, check mode)" + community.general.filesystem: + dev: '{{ dev }}' + state: absent + register: wipefs + check_mode: true + +- name: "Assert that the state did not change" + ansible.builtin.assert: + that: + - wipefs is not changed + + +# By the way, test removal of a filesystem on unexistent device +- name: "Remove filesystem (unexistent device)" + community.general.filesystem: + dev: '/dev/unexistent_device' + state: absent + register: wipefs + +- name: "Assert that the state did not change" + ansible.builtin.assert: + that: + - wipefs is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/setup.yml new file mode 100644 index 000000000..97dafaeee --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/setup.yml @@ -0,0 +1,154 @@ +--- +# 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 + +# By installing e2fsprogs on FreeBSD, we get a usable blkid command, but this +# package conflicts with util-linux, that provides blkid too, but also wipefs +# (required for filesystem state=absent). +- name: "Install filesystem tools" + ansible.builtin.package: + name: '{{ item }}' + state: present + # xfsprogs on OpenSUSE requires Python 3, skip this for our newer Py2 OpenSUSE builds + when: not (item == 'xfsprogs' and ansible_os_family == 'Suse' and ansible_python.version.major == 2 and ansible_distribution_major_version|int != 42) + loop: + - e2fsprogs + - xfsprogs + +- name: "Install btrfs progs" + ansible.builtin.package: + name: btrfs-progs + state: present + when: + - ansible_os_family != 'Suse' + - not (ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('16.04', '<=')) + - ansible_system != "FreeBSD" + - not (ansible_facts.os_family == "RedHat" and ansible_facts.distribution_major_version is version('8', '>=')) + - ansible_os_family != 'Archlinux' # TODO + +- name: "Install btrfs tools (Ubuntu <= 16.04)" + ansible.builtin.package: + name: btrfs-tools + state: present + when: + - ansible_distribution == 'Ubuntu' + - ansible_distribution_version is version('16.04', '<=') + +- name: "Install btrfs progs (OpenSuse)" + ansible.builtin.package: + name: + - python{{ ansible_python.version.major }}-xml + - btrfsprogs + state: present + when: ansible_os_family == 'Suse' + +- name: "Install reiserfs utils (Fedora)" + ansible.builtin.package: + name: reiserfs-utils + state: present + when: + - ansible_distribution == 'Fedora' and (ansible_facts.distribution_major_version | int < 35) + +- name: "Install reiserfs and util-linux-systemd (for findmnt) (OpenSuse)" + ansible.builtin.package: + name: + - reiserfs + - util-linux-systemd + state: present + when: + - ansible_os_family == 'Suse' + +- name: "Install reiserfs progs (Debian and more)" + ansible.builtin.package: + name: reiserfsprogs + state: present + when: + - ansible_system == 'Linux' + - ansible_os_family not in ['Suse', 'RedHat', 'Alpine'] + +- name: "Install reiserfs progs (FreeBSD)" + ansible.builtin.package: + name: progsreiserfs + state: present + when: + - ansible_system == 'FreeBSD' + +- name: "Install ocfs2 (Debian)" + ansible.builtin.package: + name: ocfs2-tools + state: present + when: ansible_os_family == 'Debian' + +- name: "Install f2fs tools and get version" + when: + - ansible_os_family != 'RedHat' or ansible_distribution == 'Fedora' + - ansible_distribution != 'Ubuntu' or ansible_distribution_version is version('16.04', '>=') + - ansible_system != "FreeBSD" + block: + - name: "Install f2fs tools" + ansible.builtin.package: + name: f2fs-tools + state: present + + - name: "Fetch f2fs version" + ansible.builtin.command: + cmd: mkfs.f2fs /dev/null + changed_when: false + ignore_errors: true + register: mkfs_f2fs + + - name: "Record f2fs_version" + ansible.builtin.set_fact: + f2fs_version: '{{ mkfs_f2fs.stdout + | regex_search("F2FS-tools: mkfs.f2fs Ver:.*") + | regex_replace("F2FS-tools: mkfs.f2fs Ver: ([0-9.]+) .*", "\1") }}' + +- name: "Install dosfstools and lvm2 (Linux)" + ansible.builtin.package: + name: + - dosfstools + - lvm2 + when: ansible_system == 'Linux' + +- name: "Install fatresize and get version" + when: + - ansible_system == 'Linux' + - ansible_os_family != 'Suse' + - ansible_os_family != 'RedHat' or (ansible_distribution == 'CentOS' and ansible_distribution_version is version('7.0', '==')) + - ansible_os_family != 'Alpine' + block: + - name: "Install fatresize" + ansible.builtin.package: + name: fatresize + state: present + + - name: "Fetch fatresize version" + ansible.builtin.command: + cmd: fatresize --help + changed_when: false + register: fatresize + + - name: "Record fatresize_version" + ansible.builtin.set_fact: + fatresize_version: '{{ fatresize.stdout_lines[0] | regex_search("[0-9]+\.[0-9]+\.[0-9]+") }}' + +- name: "Fetch e2fsprogs version" + ansible.builtin.command: + cmd: mke2fs -V + changed_when: false + register: mke2fs + +- name: "Record e2fsprogs_version" + ansible.builtin.set_fact: + # mke2fs 1.43.6 (29-Aug-2017) + e2fsprogs_version: '{{ mke2fs.stderr_lines[0] | regex_search("[0-9]{1,2}\.[0-9]{1,2}(\.[0-9]{1,2})?") }}' + +- name: "Set version-related facts to skip further tasks" + ansible.builtin.set_fact: + # http://e2fsprogs.sourceforge.net/e2fsprogs-release.html#1.43 + # Mke2fs no longer complains if the user tries to create a file system + # using the entire block device. + force_creation: "{{ e2fsprogs_version is version('1.43', '<') }}" + # Earlier versions have a segfault bug + resize_vfat: "{{ fatresize_version|default('0.0') is version('1.0.4', '>=') }}" diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/vars/Ubuntu-14.04.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/vars/Ubuntu-14.04.yml new file mode 100644 index 000000000..d0cc5f229 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/vars/Ubuntu-14.04.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 + +ocfs2_fssize: 108 +f2fs_fssize: 116 diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/vars/default.yml new file mode 100644 index 000000000..80151e40e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filesystem/vars/default.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 + +ocfs2_fssize: 20 diff --git a/ansible_collections/community/general/tests/integration/targets/filter_counter/aliases b/ansible_collections/community/general/tests/integration/targets/filter_counter/aliases new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_counter/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/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_counter/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_counter/tasks/main.yml new file mode 100644 index 000000000..77d6b1b02 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_counter/tasks/main.yml @@ -0,0 +1,41 @@ +--- +#################################################################### +# 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: test counter filter + assert: + that: + - "('abca' | community.general.counter) == {'a': 2, 'b': 1, 'c': 1}" + - "(['apple', 'pear', 'pear'] | community.general.counter) == {'apple': 1, 'pear': 2}" + - "([1, 2, 2, 3] | community.general.counter) == {1: 1, 2: 2, 3: 1}" + - "([1.11, 1.11, 1.12] | community.general.counter) == {1.11: 2, 1.12: 1}" + +- name: test fail argument not a sequence + debug: + msg: "{{ {'a': 'b'} | community.general.counter }}" + ignore_errors: true + register: res + +- name: verify test fail argument not a sequence + assert: + that: + - res is failed + - res.msg is match('Argument for community.general.counter must be a sequence') + +- name: test fail element not hashable + debug: + msg: "{{ [{'a': 'b'}] | community.general.counter }}" + ignore_errors: true + register: res + +- name: verify test fail element not hashable + assert: + that: + - res is failed + - res.msg is match('community.general.counter needs a sequence with hashable elements') diff --git a/ansible_collections/community/general/tests/integration/targets/filter_dict/aliases b/ansible_collections/community/general/tests/integration/targets/filter_dict/aliases new file mode 100644 index 000000000..e8051e042 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_dict/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 +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/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_dict/tasks/main.yml new file mode 100644 index 000000000..7b4cefde9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_dict/tasks/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 + +- name: "Test dict filter" + assert: + that: + - "[['a', 'b']] | community.general.dict == dict([['a', 'b']])" + - "[['a', 'b'], [1, 2]] | community.general.dict == dict([['a', 'b'], [1, 2]])" + - "[] | community.general.dict == dict([])" 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 new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/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/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_kv/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/tasks/main.yml new file mode 100644 index 000000000..47dc8e25d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/tasks/main.yml @@ -0,0 +1,14 @@ +--- +#################################################################### +# 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: test dict_kv filter + assert: + that: + - "('value' | community.general.dict_kv('key')) == {'key': 'value'}" 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 new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/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/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/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/tasks/main.yml new file mode 100644 index 000000000..5c58f85d4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/tasks/main.yml @@ -0,0 +1,54 @@ +--- +#################################################################### +# 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: Parse valid csv input + assert: + that: + - "valid_comma_separated | community.general.from_csv == expected_result" + +- name: Parse valid csv input containing spaces with/without skipinitialspace=True + assert: + that: + - "valid_comma_separated_spaces | community.general.from_csv(skipinitialspace=True) == expected_result" + - "valid_comma_separated_spaces | community.general.from_csv != expected_result" + +- name: Parse valid csv input with no headers with/without specifying fieldnames + assert: + that: + - "valid_comma_separated_no_headers | community.general.from_csv(fieldnames=['id','name','role']) == expected_result" + - "valid_comma_separated_no_headers | community.general.from_csv != expected_result" + +- name: Parse valid pipe-delimited csv input with/without delimiter=| + assert: + that: + - "valid_pipe_separated | community.general.from_csv(delimiter='|') == expected_result" + - "valid_pipe_separated | community.general.from_csv != expected_result" + +- name: Register result of invalid csv input when strict=False + debug: + var: "invalid_comma_separated | community.general.from_csv" + register: _invalid_csv_strict_false + +- name: Test invalid csv input when strict=False is successful + assert: + that: + - _invalid_csv_strict_false is success + +- name: Register result of invalid csv input when strict=True + debug: + var: "invalid_comma_separated | community.general.from_csv(strict=True)" + register: _invalid_csv_strict_true + ignore_errors: true + +- name: Test invalid csv input when strict=True is failed + assert: + that: + - _invalid_csv_strict_true is failed + - _invalid_csv_strict_true.msg is match('Unable to process file:.*') diff --git a/ansible_collections/community/general/tests/integration/targets/filter_from_csv/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/vars/main.yml new file mode 100644 index 000000000..7212c5aee --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/vars/main.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 + +valid_comma_separated: | + id,name,role + 1,foo,bar + 2,bar,baz +valid_comma_separated_spaces: | + id,name,role + 1, foo, bar + 2, bar, baz +valid_comma_separated_no_headers: | + 1,foo,bar + 2,bar,baz +valid_pipe_separated: | + id|name|role + 1|foo|bar + 2|bar|baz +invalid_comma_separated: | + id,name,role + 1,foo,bar + 2,"b"ar",baz +expected_result: + - id: '1' + name: foo + role: bar + - id: '2' + name: bar + role: baz 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 new file mode 100644 index 000000000..e8051e042 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/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 +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_groupby_as_dict/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/tasks/main.yml new file mode 100644 index 000000000..f4047f4ac --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/tasks/main.yml @@ -0,0 +1,49 @@ +--- +# 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 functionality + assert: + that: + - list1 | community.general.groupby_as_dict('name') == dict1 + +- name: 'Test error: not a list' + set_fact: + test: "{{ list_no_list | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - result.msg == 'Input is not a sequence' + +- name: 'Test error: list element not a mapping' + set_fact: + test: "{{ list_no_dict | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - "result.msg == 'Sequence element #0 is not a mapping'" + +- name: 'Test error: list element does not have attribute' + set_fact: + test: "{{ list_no_attribute | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - "result.msg == 'Attribute not contained in element #1 of sequence'" + +- name: 'Test error: attribute collision' + set_fact: + test: "{{ list_collision | community.general.groupby_as_dict('name') }}" + ignore_errors: true + register: result + +- assert: + that: + - result.msg == "Multiple sequence entries have attribute value 'a'" or result.msg == "Multiple sequence entries have attribute value u'a'" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/vars/main.yml new file mode 100644 index 000000000..74e24dd3c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/vars/main.yml @@ -0,0 +1,35 @@ +--- +# 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: + - name: a + x: y + - name: b + z: 1 + +dict1: + a: + name: a + x: y + b: + name: b + z: 1 + +list_no_list: + a: + name: a + +list_no_dict: + - [] + - 1 + +list_no_attribute: + - name: a + foo: baz + - foo: bar + +list_collision: + - name: a + - name: a diff --git a/ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases b/ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_hashids/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/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_hashids/runme.sh b/ansible_collections/community/general/tests/integration/targets/filter_hashids/runme.sh new file mode 100755 index 000000000..c7a215a06 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_hashids/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 hashids + +ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_hashids/runme.yml b/ansible_collections/community/general/tests/integration/targets/filter_hashids/runme.yml new file mode 100644 index 000000000..3ac0e388f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_hashids/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: filter_hashids } diff --git a/ansible_collections/community/general/tests/integration/targets/filter_hashids/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_hashids/tasks/main.yml new file mode 100644 index 000000000..4a76540f6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_hashids/tasks/main.yml @@ -0,0 +1,63 @@ +--- +#################################################################### +# 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: Test valid hashable inputs + assert: + that: + - "single_int | community.general.hashids_encode | community.general.hashids_decode == [single_int]" + - "int_list | community.general.hashids_encode | community.general.hashids_decode | list == int_list" + - "(1,2,3) | community.general.hashids_encode | community.general.hashids_decode == [1,2,3]" + +- name: Test valid parameters + assert: + that: + - "single_int | community.general.hashids_encode(salt='test') | community.general.hashids_decode(salt='test') == [single_int]" + - "single_int | community.general.hashids_encode(alphabet='1234567890abcdef') | community.general.hashids_decode(alphabet='1234567890abcdef') == [single_int]" + - "single_int | community.general.hashids_encode(min_length=20) | community.general.hashids_decode(min_length=20) == [single_int]" + - "single_int | community.general.hashids_encode(min_length=20) | length == 20" + +- name: Test valid unhashable inputs + assert: + that: + - "single_float | community.general.hashids_encode | community.general.hashids_decode == []" + - "arbitrary_string | community.general.hashids_encode | community.general.hashids_decode == []" + +- name: Register result of invalid salt + debug: + var: "invalid_input | community.general.hashids_encode(salt=10)" + register: invalid_salt_message + ignore_errors: true + +- name: Test invalid salt fails + assert: + that: + - invalid_salt_message is failed + +- name: Register result of invalid alphabet + debug: + var: "invalid_input | community.general.hashids_encode(alphabet='abc')" + register: invalid_alphabet_message + ignore_errors: true + +- name: Test invalid alphabet fails + assert: + that: + - invalid_alphabet_message is failed + +- name: Register result of invalid min_length + debug: + var: "invalid_input | community.general.hashids_encode(min_length='foo')" + register: invalid_min_length_message + ignore_errors: true + +- name: Test invalid min_length fails + assert: + that: + - invalid_min_length_message is failed diff --git a/ansible_collections/community/general/tests/integration/targets/filter_hashids/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_hashids/vars/main.yml new file mode 100644 index 000000000..db65ef562 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_hashids/vars/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 + +single_int: 1 +int_list: [1, 2, 3] +single_float: [2.718] +arbitrary_string: "will not hash" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_jc/aliases b/ansible_collections/community/general/tests/integration/targets/filter_jc/aliases new file mode 100644 index 000000000..0e799090e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_jc/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 +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 diff --git a/ansible_collections/community/general/tests/integration/targets/filter_jc/runme.sh b/ansible_collections/community/general/tests/integration/targets/filter_jc/runme.sh new file mode 100755 index 000000000..c427b8b35 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_jc/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 jc + +ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_jc/runme.yml b/ansible_collections/community/general/tests/integration/targets/filter_jc/runme.yml new file mode 100644 index 000000000..6d6a89c00 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_jc/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: filter_jc } diff --git a/ansible_collections/community/general/tests/integration/targets/filter_jc/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_jc/tasks/main.yml new file mode 100644 index 000000000..a06a0bfa4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_jc/tasks/main.yml @@ -0,0 +1,14 @@ +--- +#################################################################### +# 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: test jc key/value parser + assert: + that: + - "('key1=value1\nkey2=value2' | community.general.jc('kv')) == {'key1': 'value1', 'key2': 'value2'}" 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 new file mode 100644 index 000000000..cee9abd2c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_json_query/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 +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_json_query/runme.sh b/ansible_collections/community/general/tests/integration/targets/filter_json_query/runme.sh new file mode 100755 index 000000000..b1fa994b3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_json_query/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 jmespath + +ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_json_query/runme.yml b/ansible_collections/community/general/tests/integration/targets/filter_json_query/runme.yml new file mode 100644 index 000000000..28281cffb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_json_query/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: filter_json_query } diff --git a/ansible_collections/community/general/tests/integration/targets/filter_json_query/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_json_query/tasks/main.yml new file mode 100644 index 000000000..92db6d876 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_json_query/tasks/main.yml @@ -0,0 +1,14 @@ +--- +#################################################################### +# 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: Test json_query filter + assert: + that: + - "users | community.general.json_query('[*].hosts[].host') == ['host_a', 'host_b', 'host_c', 'host_d']" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_json_query/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_json_query/vars/main.yml new file mode 100644 index 000000000..1edd723be --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_json_query/vars/main.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 + +users: + - name: steve + hosts: + - host: host_a + password: abc + - host: host_b + - name: bill + hosts: + - host: host_c + password: default + - host: host_d 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 new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/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/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_lists_mergeby/tasks/lists_mergeby_2-10.yml b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_2-10.yml new file mode 100644 index 000000000..62896e1b0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_2-10.yml @@ -0,0 +1,143 @@ +--- +# 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: 101.Merge 2 lists by attribute name. list_merge='keep' + block: + - name: Merge 2 lists by attribute name. list_merge='keep'. set + set_fact: + my_list: "{{ [list100, list101]| + community.general.lists_mergeby('name', list_merge='keep') }}" + - name: Merge 2 lists by attribute name. list_merge='keep'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result101): + {{ my_list|difference(result101)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge 2 lists by attribute name. list_merge='keep'. assert + assert: + that: my_list | difference(result101) | length == 0 + tags: t101 + +- name: 102.Merge 2 lists by attribute name. list_merge='append' + block: + - name: Merge 2 lists by attribute name. list_merge='append'. set + set_fact: + my_list: "{{ [list100, list101]| + community.general.lists_mergeby('name', list_merge='append') }}" + - name: Merge 2 lists by attribute name. list_merge='append'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result102): + {{ my_list|difference(result102)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge 2 lists by attribute name. list_merge='append'. assert + assert: + that: my_list | difference(result102) | length == 0 + tags: t102 + +- name: 103.Merge 2 lists by attribute name. list_merge='prepend' + block: + - name: Merge 2 lists by attribute name. list_merge='prepend'. set + set_fact: + my_list: "{{ [list100, list101]| + community.general.lists_mergeby('name', list_merge='prepend') }}" + - name: Merge 2 lists by attribute name. list_merge='prepend'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result103): + {{ my_list|difference(result103)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge 2 lists by attribute name. list_merge='prepend'. assert + assert: + that: my_list | difference(result103) | length == 0 + tags: t103 + +- name: 104.Merge 2 lists by attribute name. list_merge='append_rp' + block: + - name: Merge 2 lists by attribute name. list_merge='append_rp'. set + set_fact: + my_list: "{{ [list102, list103]| + community.general.lists_mergeby('name', list_merge='append_rp') }}" + - name: Merge 2 lists by attribute name. list_merge='append_rp'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result104): + {{ my_list|difference(result104)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge 2 lists by attribute name. list_merge='append_rp'. assert + assert: + that: my_list | difference(result104) | length == 0 + tags: t104 + +- name: 105.Merge 2 lists by attribute name. list_merge='prepend_rp' + block: + - name: Merge 2 lists by attribute name. list_merge='prepend_rp'. set + set_fact: + my_list: "{{ [list102, list103]| + community.general.lists_mergeby('name', list_merge='prepend_rp') }}" + - name: Merge 2 lists by attribute name. list_merge='prepend_rp'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result105): + {{ my_list|difference(result105)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge 2 lists by attribute name. list_merge='prepend_rp'. assert + assert: + that: my_list | difference(result105) | length == 0 + tags: t105 + +# Test recursive + +- name: 200.Merge by name. recursive=True list_merge='append_rp' + block: + - name: Merge by name. recursive=True list_merge='append_rp'. set + set_fact: + my_list: "{{ [list200, list201]| + community.general.lists_mergeby('name', + recursive=True, + list_merge='append_rp') }}" + - name: Merge by name. recursive=True list_merge='append_rp'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result200): + {{ my_list|difference(result200)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge by name. recursive=True list_merge='append_rp'. assert + assert: + that: my_list | difference(result200) | length == 0 + tags: t200 + +- name: 201.Merge by name. recursive=False list_merge='append_rp' + block: + - name: Merge by name. recursive=False list_merge='append_rp'. set + set_fact: + my_list: "{{ [list200, list201]| + community.general.lists_mergeby('name', + recursive=False, + list_merge='append_rp') }}" + - name: Merge by name. recursive=False list_merge='append_rp'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result201): + {{ my_list|difference(result201)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge by name. recursive=False list_merge='append_rp'. assert + assert: + that: my_list | difference(result201) | length == 0 + tags: t201 diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_default.yml b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_default.yml new file mode 100644 index 000000000..93917c97c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/lists_mergeby_default.yml @@ -0,0 +1,169 @@ +--- +# 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 + debug: + var: ansible_version + when: debug_test|d(false)|bool + tags: t0 + +- name: 1. Test lists merged by attribute name + block: + - name: Test lists merged by attribute name debug + debug: + msg: "{{ list1 | community.general.lists_mergeby(list2, 'name') }}" + when: debug_test|d(false)|bool + - name: Test lists merged by attribute name assert + assert: + that: + - "(list1 | community.general.lists_mergeby(list2, 'name') | list | + difference(list3) | length) == 0" + tags: t1 + +- name: 2.Test list1 empty + block: + - name: Test list1 empty debug + debug: + msg: "{{ [] | community.general.lists_mergeby(list2, 'name') }}" + when: debug_test|d(false)|bool + - name: Test list1 empty assert + assert: + that: + - "([] | community.general.lists_mergeby(list2, 'name') | list | + difference(list2) | length) == 0" + tags: t2 + +- name: 3.Test all lists empty + block: + - name: Test all lists empty debug + debug: + msg: "{{ [] | community.general.lists_mergeby([], 'name') }}" + when: debug_test|d(false)|bool + - name: Test all lists empty assert + assert: + that: + - "([] | community.general.lists_mergeby([], 'name') | list | + length) == 0" + tags: t3 + +- name: 4.First argument must be list + block: + - name: First argument must be list set + set_fact: + my_list: "{{ {'x': 'y'} | community.general.lists_mergeby(list2, 'name') }}" + register: result + ignore_errors: true + - name: First argument must be list debug + debug: + var: my_list + when: debug_test|d(false)|bool + - name: First argument must be list assert + assert: + that: + - result is failed + - '"All arguments before the argument index for community.general.lists_mergeby must be lists." in result.msg' + tags: t4 + +- name: 5.Second argument must be list + block: + - name: Second argument must be list set + set_fact: + my_list: "{{ list1 | community.general.lists_mergeby({'x': 'y'}, 'name') }}" + register: result + ignore_errors: true + - name: Second argument must be list set debug + debug: + var: my_list + when: debug_test|d(false)|bool + - name: Second argument must be list set assert + assert: + that: + - result is failed + - '"All arguments before the argument index for community.general.lists_mergeby must be lists." in result.msg' + tags: t5 + +- name: 6.First arguments after the lists must be string + block: + - name: First arguments after the lists must be string set + set_fact: + my_list: "{{ list1 | community.general.lists_mergeby(list2, {'x': 'y'}) }}" + register: result + ignore_errors: true + - name: First arguments after the lists must be string debug + debug: + var: my_list + when: debug_test|d(false)|bool + - name: First arguments after the lists must be string assert + assert: + that: + - result is failed + - '"First argument after the lists for community.general.lists_mergeby must be string." in result.msg' + tags: t6 + +- name: 7.Elements of list must be dictionaries + block: + - name: Elements of list must be dictionaries set + set_fact: + my_list: "{{ list4 | community.general.lists_mergeby(list2, 'name') }}" + register: result + ignore_errors: true + - name: Elements of list must be dictionaries debug + debug: + var: my_list + when: debug_test|d(false)|bool + - name: Elements of list must be dictionaries assert + assert: + that: + - result is failed + - '"Elements of list arguments for lists_mergeby must be dictionaries." in result.msg' + tags: t7 + +- name: 8.Merge 3 lists by attribute name. 1 list in params. + block: + - name: Merge 3 lists by attribute name. 1 list in params. set + set_fact: + my_list: "{{ [list1, list2] | community.general.lists_mergeby(list5, 'name') }}" + - name: Merge 3 lists by attribute name. 1 list in params. debug + debug: + var: my_list + when: debug_test|d(false)|bool + - name: Merge 3 lists by attribute name. 1 list in params. assert + assert: + that: my_list | difference(result1) | length == 0 + tags: t8 + +- name: 9.Merge 3 lists by attribute name. No list in the params. + block: + - name: Merge 3 lists by attribute name. No list in the params. set + set_fact: + my_list: "{{ [list1, list2, list5] | community.general.lists_mergeby('name') }}" + - name: Merge 3 lists by attribute name. No list in the params. debug + debug: + var: my_list + when: debug_test|d(false)|bool + - name: Merge 3 lists by attribute name. No list in the params. asset + assert: + that: my_list | difference(result1) | length == 0 + tags: t9 + +# Test list_merge default options + +- name: 100.Merge 2 lists by attribute name. list_merge='replace' + block: + - name: Merge 2 lists by attribute name. list_merge='replace'. set + set_fact: + my_list: "{{ [list100, list101] | community.general.lists_mergeby('name') }}" + - name: Merge 2 lists by attribute name. list_merge='replace'. debug + debug: + msg: |- + my_list: + {{ my_list|to_nice_yaml|indent(2) }} + my_list|difference(result100): + {{ my_list|difference(result100)|to_nice_yaml|indent(2) }} + when: debug_test|d(false)|bool + - name: Merge 2 lists by attribute name. list_merge='replace'. assert + assert: + that: my_list | difference(result100) | length == 0 + tags: t100 diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/main.yml new file mode 100644 index 000000000..d0bda368c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/tasks/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 + +- name: Test list_merge default options + import_tasks: lists_mergeby_default.yml + +- name: Test list_merge non-default options in Ansible 2.10 and higher + import_tasks: lists_mergeby_2-10.yml + when: ansible_version.full is version('2.10', '>=') diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/vars/main.yml new file mode 100644 index 000000000..f3b492878 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/vars/main.yml @@ -0,0 +1,209 @@ +--- +# 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: + - name: myname01 + param01: myparam01 + - name: myname02 + param01: myparam02 + +list2: + - name: myname01 + param01: myparam03 + - name: myname02 + param02: myparam04 + - name: myname03 + param03: myparam03 + +list3: + - name: myname01 + param01: myparam03 + - name: myname02 + param01: myparam02 + param02: myparam04 + - name: myname03 + param03: myparam03 + +list4: + - name: myname01 + param01: myparam01 + - myname02 + +list5: + - name: myname01 + param01: myparam05 + - name: myname02 + param01: myparam06 + +result1: + - name: myname01 + param01: myparam05 + - name: myname02 + param01: myparam06 + param02: myparam04 + - name: myname03 + param03: myparam03 + +# Test list_merge + +list100: + - name: myname01 + param01: + - default1 + - name: myname02 + param01: + - default2 + +list101: + - name: myname01 + param01: + - patch1 + - name: myname02 + param01: + - patch2 + +list102: + - name: myname01 + param01: + - patch1a + - patch1b + - patch1c + - name: myname02 + param01: + - patch2a + - patch2b + - patch2d + +list103: + - name: myname01 + param01: + - patch1c + - patch1d + - name: myname02 + param01: + - patch2c + - patch2d + +result100: + - name: myname01 + param01: + - patch1 + - name: myname02 + param01: + - patch2 + +result101: + - name: myname01 + param01: + - default1 + - name: myname02 + param01: + - default2 + +result102: + - name: myname01 + param01: + - default1 + - patch1 + - name: myname02 + param01: + - default2 + - patch2 + +result103: + - name: myname01 + param01: + - patch1 + - default1 + - name: myname02 + param01: + - patch2 + - default2 + +result104: + - name: myname01 + param01: + - patch1a + - patch1b + - patch1c + - patch1d + - name: myname02 + param01: + - patch2a + - patch2b + - patch2c + - patch2d + +result105: + - name: myname01 + param01: + - patch1c + - patch1d + - patch1a + - patch1b + - name: myname02 + param01: + - patch2c + - patch2d + - patch2a + - patch2b + +# Test recursive + +list200: + - name: myname01 + param01: + x: default_value + y: default_value + list: + - default_value + - name: myname02 + param01: [1, 1, 2, 3] + +list201: + - name: myname01 + param01: + y: patch_value + z: patch_value + list: + - patch_value + - name: myname02 + param01: [3, 4, 4, {key: value}] + +result200: + - name: myname01 + param01: + list: + - default_value + - patch_value + x: default_value + y: patch_value + z: patch_value + - name: myname02 + param01: + - 1 + - 1 + - 2 + - 3 + - 4 + - 4 + - key: value + +result201: + - name: myname01 + param01: + list: + - patch_value + y: patch_value + z: patch_value + - name: myname02 + param01: + - 1 + - 1 + - 2 + - 3 + - 4 + - 4 + - key: value 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 new file mode 100644 index 000000000..51baa3d7a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/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/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_path_join_shim/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/tasks/main.yml new file mode 100644 index 000000000..1462656fb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/tasks/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 + +- name: "Test path_join filter" + assert: + that: + - "['a', 'b'] | community.general.path_join == 'a/b'" + - "['a', '/b'] | community.general.path_join == '/b'" + - "[''] | community.general.path_join == ''" 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 new file mode 100644 index 000000000..cee9abd2c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/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 +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_random_mac/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/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/filter_random_mac/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/tasks/main.yml new file mode 100644 index 000000000..230f9776d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/tasks/main.yml @@ -0,0 +1,62 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# test code for filters +# Copyright (c) 2014, Michael DeHaan +# Copyright (c) 2019, 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 random_mac filter bad argument type + debug: + var: "0 | community.general.random_mac" + register: _bad_random_mac_filter + ignore_errors: true + +- name: Verify random_mac filter showed a bad argument type error message + assert: + that: + - _bad_random_mac_filter is failed + - "_bad_random_mac_filter.msg is match('Invalid value type (.*int.*) for random_mac .*')" + +- name: Test random_mac filter bad argument value + debug: + var: "'dummy' | community.general.random_mac" + register: _bad_random_mac_filter + ignore_errors: true + +- name: Verify random_mac filter showed a bad argument value error message + assert: + that: + - _bad_random_mac_filter is failed + - "_bad_random_mac_filter.msg is match('Invalid value (.*) for random_mac: .* not hexa byte')" + +- name: Test random_mac filter prefix too big + debug: + var: "'00:00:00:00:00:00' | community.general.random_mac" + register: _bad_random_mac_filter + ignore_errors: true + +- name: Verify random_mac filter showed a prefix too big error message + assert: + that: + - _bad_random_mac_filter is failed + - "_bad_random_mac_filter.msg is match('Invalid value (.*) for random_mac: 5 colon.* separated items max')" + +- name: Verify random_mac filter + assert: + that: + - "'00' | community.general.random_mac is match('^00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" + - "'00:00' | community.general.random_mac is match('^00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" + - "'00:00:00' | community.general.random_mac is match('^00:00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" + - "'00:00:00:00' | community.general.random_mac is match('^00:00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" + - "'00:00:00:00:00' | community.general.random_mac is match('^00:00:00:00:00:[a-f0-9][a-f0-9]$')" + - "'00:00:00' | community.general.random_mac != '00:00:00' | community.general.random_mac" + +- name: Verify random_mac filter with seed + assert: + that: + - "'00:00:00' | community.general.random_mac(seed='test') == '00:00:00' | community.general.random_mac(seed='test')" + - "'00:00:00' | community.general.random_mac(seed='test') != '00:00:00' | community.general.random_mac(seed='another_test')" diff --git a/ansible_collections/community/general/tests/integration/targets/filter_time/aliases b/ansible_collections/community/general/tests/integration/targets/filter_time/aliases new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_time/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/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_time/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_time/tasks/main.yml new file mode 100644 index 000000000..3b6539499 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_time/tasks/main.yml @@ -0,0 +1,115 @@ +--- +#################################################################### +# 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: test zero is 0 + assert: + that: + - "('0' | community.general.to_milliseconds) == 0" + - "('0' | community.general.to_seconds) == 0" + - "('0' | community.general.to_minutes) == 0" + +- name: test to_milliseconds filter + assert: + that: + - "('1000ms' | community.general.to_milliseconds) == 1000" + - "('1s' | community.general.to_milliseconds) == 1000" + - "('1m' | community.general.to_milliseconds) == 60000" + +- name: test to_seconds filter + assert: + that: + - "('1000msecs' | community.general.to_seconds) == 1" + - "('1ms' | community.general.to_seconds) == 0.001" + - "('12m' | community.general.to_seconds) == 720" + - "('300minutes' | community.general.to_seconds) == 18000" + - "('3h 12m' | community.general.to_seconds) == 11520" + - "('2days 3hours 12mins 15secs' | community.general.to_seconds) == 184335" + - "('2d -2d -12s' | community.general.to_seconds) == -12" + +- name: test to_minutes filter + assert: + that: + - "('30s' | community.general.to_minutes) == 0.5" + - "('12m' | community.general.to_minutes) == 12" + - "('3h 72m' | community.general.to_minutes) == 252" + - "('300s' | community.general.to_minutes) == 5" + +- name: test to_hours filter + assert: + that: + - "('30m' | community.general.to_hours) == 0.5" + - "('3h 119m 61s' | community.general.to_hours) > 5" + +- name: test to_days filter + assert: + that: + - "('1year' | community.general.to_days) == 365" + - "('1week' | community.general.to_days) == 7" + - "('2weeks' | community.general.to_days) == 14" + - "('1mo' | community.general.to_days) == 30" + - "('1mo' | community.general.to_days(month=28)) == 28" + +- name: test to_weeks filter + assert: + that: + - "('1y' | community.general.to_weeks | int) == 52" + - "('7d' | community.general.to_weeks) == 1" + - "('1mo' | community.general.to_weeks(month=28)) == 4" + +- name: test to_months filter + assert: + that: + - "('30d' | community.general.to_months) == 1" + - "('1year' | community.general.to_months | int) == 12" + - "('5years' | community.general.to_months(month=30, year=360)) == 60" + - "('1years' | community.general.to_months(month=2, year=34)) == 17" + +- name: test to_years filter + assert: + that: + - "('365d' | community.general.to_years | int) == 1" + - "('12mo' | community.general.to_years | round(0, 'ceil')) == 1" + - "('24mo' | community.general.to_years(month=30, year=360)) == 2" + +- name: test fail unknown unit + debug: + msg: "{{ '1s' | community.general.to_time_unit('lightyears') }}" + ignore_errors: true + register: res + +- name: verify test fail unknown unit + assert: + that: + - res is failed + - "'to_time_unit() can not convert to the following unit: lightyears' in res.msg" + +- name: test fail unknown string + debug: + msg: "{{ '1 s' | community.general.to_time_unit('s') }}" + ignore_errors: true + register: res + +- name: test fail unknown string + assert: + that: + - res is failed + - "'to_time_unit() can not interpret following string' in res.msg" + +- name: test fail unknown kwarg + debug: + msg: "{{ '1s' | community.general.to_time_unit('s', second=23) }}" + ignore_errors: true + register: res + +- name: test fail unknown kwarg + assert: + that: + - res is failed + - "'to_time_unit() got unknown keyword arguments' in res.msg" 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 new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/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/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_unicode_normalize/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/tasks/main.yml new file mode 100644 index 000000000..13902706e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/tasks/main.yml @@ -0,0 +1,44 @@ +--- +#################################################################### +# 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: Test 'NFC' normalization + assert: + that: + - u_umlaut != u_umlaut_combining + - u_umlaut_combining != (u_umlaut_combining | community.general.unicode_normalize) + - u_umlaut == (u_umlaut_combining | community.general.unicode_normalize) + +- name: Test 'NFKC' normalization + assert: + that: + - latin_capital_i != roman_numeral_one + - latin_capital_i == (roman_numeral_one | community.general.unicode_normalize(form='NFKC')) + +- name: Register invalid input type + debug: + msg: "{{ 1 | community.general.unicode_normalize }}" + ignore_errors: true + register: invalid_input_type + +- name: Assert an invalid input type causes failure + assert: + that: + - invalid_input_type is failed + +- name: Register invalid form selection + debug: + msg: "{{ 'arbitrary text' | community.general.unicode_normalize(form='invalid') }}" + ignore_errors: true + register: invalid_form_selection + +- name: Assert invalid form selection causes failure + assert: + that: + - invalid_form_selection is failed diff --git a/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/vars/main.yml new file mode 100644 index 000000000..ed4e2968b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/vars/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 + +u_umlaut: "{{ '\u00fc' }}" +u_umlaut_combining: "{{ 'u' + '\u0308' }}" +roman_numeral_one: "{{ '\u2160' }}" +latin_capital_i: "{{ '\u0049' }}" 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 new file mode 100644 index 000000000..bc9b4bc99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_version_sort/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/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/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_version_sort/tasks/main.yml new file mode 100644 index 000000000..08985d1ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/filter_version_sort/tasks/main.yml @@ -0,0 +1,14 @@ +--- +#################################################################### +# 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: validate that versions are properly sorted in a stable way + assert: + that: + - "['a-1.9.rpm', 'a-1.10-1.rpm', 'a-1.09.rpm', 'b-1.01.rpm', 'a-2.1-0.rpm', 'a-1.10-0.rpm'] | community.general.version_sort == ['a-1.9.rpm', 'a-1.09.rpm', 'a-1.10-0.rpm', 'a-1.10-1.rpm', 'a-2.1-0.rpm', 'b-1.01.rpm']" diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/aliases b/ansible_collections/community/general/tests/integration/targets/flatpak/aliases new file mode 100644 index 000000000..e462ed8cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/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/3 +destructive +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/files/serve.py b/ansible_collections/community/general/tests/integration/targets/flatpak/files/serve.py new file mode 100644 index 000000000..93df1036e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/files/serve.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# 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 + +# -*- coding: utf-8 -*- +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import posixpath +import sys + +try: + from http.server import SimpleHTTPRequestHandler, HTTPServer + from urllib.parse import unquote +except ImportError: + from SimpleHTTPServer import SimpleHTTPRequestHandler + from BaseHTTPServer import HTTPServer + from urllib import unquote + + +# Argument parsing +if len(sys.argv) != 4: + print('Syntax: {0} '.format(sys.argv[0])) + sys.exit(-1) + +HOST, PORT, PATH = sys.argv[1:4] +PORT = int(PORT) + + +# The HTTP request handler +class Handler(SimpleHTTPRequestHandler): + def translate_path(self, path): + # Modified from Python 3.6's version of SimpleHTTPRequestHandler + # to support using another base directory than CWD. + + # abandon query parameters + path = path.split('?', 1)[0] + path = path.split('#', 1)[0] + # Don't forget explicit trailing slash when normalizing. Issue17324 + trailing_slash = path.rstrip().endswith('/') + try: + path = unquote(path, errors='surrogatepass') + except (UnicodeDecodeError, TypeError) as exc: + path = unquote(path) + path = posixpath.normpath(path) + words = path.split('/') + words = filter(None, words) + path = PATH + for word in words: + if os.path.dirname(word) or word in (os.curdir, os.pardir): + # Ignore components that are not a simple file/directory name + continue + path = os.path.join(path, word) + if trailing_slash: + path += '/' + return path + + +# Run simple HTTP server +httpd = HTTPServer((HOST, PORT), Handler) + +try: + httpd.serve_forever() +except KeyboardInterrupt: + pass + +httpd.server_close() diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/flatpak/meta/main.yml new file mode 100644 index 000000000..0ac87654d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/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_flatpak_remote diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/check_mode.yml b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/check_mode.yml new file mode 100644 index 000000000..9f52dc122 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/check_mode.yml @@ -0,0 +1,197 @@ +--- +# 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 + +# - Tests with absent flatpak -------------------------------------------------- + +# state=present on absent flatpak + +- name: Test addition of absent flatpak (check mode) + flatpak: + name: com.dummy.App1 + remote: dummy-remote + state: present + register: addition_result + check_mode: true + +- name: Verify addition of absent flatpak test result (check mode) + assert: + that: + - addition_result is changed + msg: "Adding an absent flatpak shall mark module execution as changed" + +- name: Test non-existent idempotency of addition of absent flatpak (check mode) + flatpak: + name: com.dummy.App1 + remote: dummy-remote + state: present + register: double_addition_result + check_mode: true + +- name: Verify non-existent idempotency of addition of absent flatpak test result (check mode) + assert: + that: + - double_addition_result is changed + msg: | + Adding an absent flatpak a second time shall still mark module execution + as changed in check mode + +# state=absent on absent flatpak + +- name: Test removal of absent flatpak check mode + flatpak: + name: com.dummy.App1 + state: absent + register: removal_result + check_mode: true + +- name: Verify removal of absent flatpak test result (check mode) + assert: + that: + - removal_result is not changed + msg: "Removing an absent flatpak shall mark module execution as not changed" + +# state=present with url on absent flatpak + +- name: Test addition of absent flatpak with url (check mode) + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote + state: present + register: url_addition_result + check_mode: true + +- name: Verify addition of absent flatpak with url test result (check mode) + assert: + that: + - url_addition_result is changed + msg: "Adding an absent flatpak from URL shall mark module execution as changed" + +- name: Test non-existent idempotency of addition of absent flatpak with url (check mode) + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote + state: present + register: double_url_addition_result + check_mode: true + +- name: > + Verify non-existent idempotency of additionof absent flatpak with url test + result (check mode) + assert: + that: + - double_url_addition_result is changed + msg: | + Adding an absent flatpak from URL a second time shall still mark module execution + as changed in check mode + +# state=absent with url on absent flatpak + +- name: Test removal of absent flatpak with url not doing anything (check mode) + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: absent + register: url_removal_result + check_mode: true + +- name: Verify removal of absent flatpak with url test result (check mode) + assert: + that: + - url_removal_result is not changed + msg: "Removing an absent flatpak shall mark module execution as not changed" + +# - Tests with present flatpak ------------------------------------------------- + +# state=present on present flatpak + +- name: Test addition of present flatpak (check mode) + flatpak: + name: com.dummy.App2 + remote: dummy-remote + state: present + register: addition_present_result + check_mode: true + +- name: Verify addition test result of present flatpak (check mode) + assert: + that: + - addition_present_result is not changed + msg: "Adding an present flatpak shall mark module execution as not changed" + +# state=absent on present flatpak + +- name: Test removal of present flatpak (check mode) + flatpak: + name: com.dummy.App2 + state: absent + register: removal_present_result + check_mode: true + +- name: Verify removal of present flatpak test result (check mode) + assert: + that: + - removal_present_result is changed + msg: "Removing a present flatpak shall mark module execution as changed" + +- name: Test non-existent idempotency of removal (check mode) + flatpak: + name: com.dummy.App2 + state: absent + register: double_removal_present_result + check_mode: true + +- name: Verify non-existent idempotency of removal (check mode) + assert: + that: + - double_removal_present_result is changed + msg: | + Removing a present flatpak a second time shall still mark module execution + as changed in check mode + +# state=present with url on present flatpak + +- name: Test addition with url of present flatpak (check mode) + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + remote: dummy-remote + state: present + register: url_addition_present_result + check_mode: true + +- name: Verify addition with url of present flatpak test result (check mode) + assert: + that: + - url_addition_present_result is not changed + msg: "Adding a present flatpak from URL shall mark module execution as not changed" + +# state=absent with url on present flatpak + +- name: Test removal with url of present flatpak (check mode) + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + state: absent + register: url_removal_present_result + check_mode: true + +- name: Verify removal with url of present flatpak test result (check mode) + assert: + that: + - url_removal_present_result is changed + msg: "Removing an absent flatpak shall mark module execution as not changed" + +- name: Test non-existent idempotency of removal with url of present flatpak (check mode) + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + remote: dummy-remote + state: absent + register: double_url_removal_present_result + check_mode: true + +- name: > + Verify non-existent idempotency of installation with url of present + flatpak test result (check mode) + assert: + that: + - double_url_removal_present_result is changed + msg: Removing an absent flatpak a second time shall still mark module execution as changed diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/main.yml new file mode 100644 index 000000000..deaf354e8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/main.yml @@ -0,0 +1,64 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2018, Alexander Bethke +# Copyright (c) 2018, 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: + + - import_tasks: setup.yml + become: true + + # executable override + + - name: Test executable override + flatpak: + name: com.dummy.App1 + remote: dummy-remote + state: present + executable: nothing-that-exists + ignore_errors: true + register: executable_override_result + + - name: Verify executable override test result + assert: + that: + - executable_override_result is failed + - executable_override_result is not changed + msg: "Specifying non-existing executable shall fail module execution" + + - import_tasks: check_mode.yml + become: false + + - import_tasks: test.yml + become: false + vars: + method: user + + - import_tasks: test.yml + become: true + vars: + method: system + + always: + + - name: Check HTTP server status + async_status: + jid: "{{ webserver_status.ansible_job_id }}" + ignore_errors: true + + - name: List processes + command: ps aux + + - name: Stop HTTP server + command: >- + pkill -f -- '{{ remote_tmp_dir }}/serve.py' + + when: | + ansible_distribution == 'Fedora' or + ansible_distribution == 'Ubuntu' and not ansible_distribution_major_version | int < 16 diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/setup.yml new file mode 100644 index 000000000..4dfdd68cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/setup.yml @@ -0,0 +1,68 @@ +--- +# 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 flatpak on Fedora + dnf: + name: flatpak + state: present + become: true + when: ansible_distribution == 'Fedora' + +- block: + - name: Activate flatpak ppa on Ubuntu + apt_repository: + repo: ppa:alexlarsson/flatpak + state: present + mode: '0644' + when: ansible_lsb.major_release | int < 18 + + - name: Install flatpak package on Ubuntu + apt: + name: flatpak + state: present + + when: ansible_distribution == 'Ubuntu' + +- name: Install dummy remote for user + flatpak_remote: + name: dummy-remote + state: present + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + method: user + +- name: Install dummy remote for system + flatpak_remote: + name: dummy-remote + state: present + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + method: system + +- name: Remove (if necessary) flatpak for testing check mode on absent flatpak + flatpak: + name: + - com.dummy.App1 + - com.dummy.App3 + remote: dummy-remote + state: absent + no_dependencies: true + +- name: Add flatpak for testing check mode on present flatpak + flatpak: + name: com.dummy.App2 + remote: dummy-remote + state: present + no_dependencies: true + +- name: Copy HTTP server + copy: + src: serve.py + dest: '{{ remote_tmp_dir }}/serve.py' + mode: '0755' + +- name: Start HTTP server + command: '{{ ansible_python.executable }} {{ remote_tmp_dir }}/serve.py 127.0.0.1 8000 /tmp/flatpak/' + async: 120 + poll: 0 + register: webserver_status diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/test.yml new file mode 100644 index 000000000..29c4efbe9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak/tasks/test.yml @@ -0,0 +1,289 @@ +--- +# 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 + +# state=present + +- name: Test addition - {{ method }} + flatpak: + name: com.dummy.App1 + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: addition_result + +- name: Verify addition test result - {{ method }} + assert: + that: + - addition_result is changed + msg: "state=present shall add flatpak when absent" + +- name: Test idempotency of addition - {{ method }} + flatpak: + name: com.dummy.App1 + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: double_addition_result + +- name: Verify idempotency of addition test result - {{ method }} + assert: + that: + - double_addition_result is not changed + msg: "state=present shall not do anything when flatpak is already present" + +# state=absent + +- name: Test removal - {{ method }} + flatpak: + name: com.dummy.App1 + state: absent + method: "{{ method }}" + no_dependencies: true + register: removal_result + +- name: Verify removal test result - {{ method }} + assert: + that: + - removal_result is changed + msg: "state=absent shall remove flatpak when present" + +- name: Test idempotency of removal - {{ method }} + flatpak: + name: com.dummy.App1 + state: absent + method: "{{ method }}" + no_dependencies: true + register: double_removal_result + +- name: Verify idempotency of removal test result - {{ method }} + assert: + that: + - double_removal_result is not changed + msg: "state=absent shall not do anything when flatpak is not present" + +# state=present with url as name + +- name: Test addition with url - {{ method }} + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: url_addition_result + +- name: Verify addition test result - {{ method }} + assert: + that: + - url_addition_result is changed + msg: "state=present with url as name shall add flatpak when absent" + +- name: Test idempotency of addition with url - {{ method }} + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: double_url_addition_result + +- name: Verify idempotency of addition with url test result - {{ method }} + assert: + that: + - double_url_addition_result is not changed + msg: "state=present with url as name shall not do anything when flatpak is already present" + +# state=absent with url as name + +- name: Test removal with url - {{ method }} + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: absent + method: "{{ method }}" + no_dependencies: true + register: url_removal_result + ignore_errors: true + +- name: Verify removal test result failed - {{ method }} + # It looks like flatpak has a bug when the hostname contains a port. If this is the case, it emits + # the following message, which we check for. If another error happens, we fail. + # Upstream issue: https://github.com/flatpak/flatpak/issues/4307 + # (The second message happens with Ubuntu 18.04.) + assert: + that: + - >- + url_removal_result.msg in [ + "error: Invalid branch 127.0.0.1:8000: Branch can't contain :", + "error: Invalid id http:: Name can't contain :", + ] + when: url_removal_result is failed + +- when: url_removal_result is not failed + block: + + - name: Verify removal test result - {{ method }} + assert: + that: + - url_removal_result is changed + msg: "state=absent with url as name shall remove flatpak when present" + + - name: Test idempotency of removal with url - {{ method }} + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: absent + method: "{{ method }}" + no_dependencies: true + register: double_url_removal_result + + - name: Verify idempotency of removal with url test result - {{ method }} + assert: + that: + - double_url_removal_result is not changed + msg: "state=absent with url as name shall not do anything when flatpak is not present" + +- name: Make sure flatpak is really gone - {{ method }} + flatpak: + name: com.dummy.App1 + state: absent + method: "{{ method }}" + no_dependencies: true + +# state=present with list of packages + +- name: Test addition with list - {{ method }} + flatpak: + name: + - com.dummy.App1 + - http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: addition_result + +- name: Verify addition with list test result - {{ method }} + assert: + that: + - addition_result is changed + msg: "state=present shall add flatpak when absent" + +- name: Test idempotency of addition with list - {{ method }} + flatpak: + name: + - com.dummy.App1 + - http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: double_addition_result + +- name: Verify idempotency of addition with list test result - {{ method }} + assert: + that: + - double_addition_result is not changed + msg: "state=present shall not do anything when flatpak is already present" + +- name: Test addition with list partially installed - {{ method }} + flatpak: + name: + - com.dummy.App1 + - http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + - com.dummy.App3 + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: addition_result + +- name: Verify addition with list partially installed test result - {{ method }} + assert: + that: + - addition_result is changed + msg: "state=present shall add flatpak when absent" + +- name: Test idempotency of addition with list partially installed - {{ method }} + flatpak: + name: + - com.dummy.App1 + - http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + - com.dummy.App3 + remote: dummy-remote + state: present + method: "{{ method }}" + no_dependencies: true + register: double_addition_result + +- name: Verify idempotency of addition with list partially installed test result - {{ method }} + assert: + that: + - double_addition_result is not changed + msg: "state=present shall not do anything when flatpak is already present" + +# state=absent with list of packages + +- name: Test removal with list - {{ method }} + flatpak: + name: + - com.dummy.App1 + - com.dummy.App2 + state: absent + method: "{{ method }}" + register: removal_result + +- name: Verify removal with list test result - {{ method }} + assert: + that: + - removal_result is changed + msg: "state=absent shall remove flatpak when present" + +- name: Test idempotency of removal with list - {{ method }} + flatpak: + name: + - com.dummy.App1 + - com.dummy.App2 + state: absent + method: "{{ method }}" + register: double_removal_result + +- name: Verify idempotency of removal with list test result - {{ method }} + assert: + that: + - double_removal_result is not changed + msg: "state=absent shall not do anything when flatpak is not present" + +- name: Test removal with list partially removed - {{ method }} + flatpak: + name: + - com.dummy.App1 + - com.dummy.App2 + - com.dummy.App3 + state: absent + method: "{{ method }}" + register: removal_result + +- name: Verify removal with list partially removed test result - {{ method }} + assert: + that: + - removal_result is changed + msg: "state=absent shall remove flatpak when present" + +- name: Test idempotency of removal with list partially removed - {{ method }} + flatpak: + name: + - com.dummy.App1 + - com.dummy.App2 + - com.dummy.App3 + state: absent + method: "{{ method }}" + register: double_removal_result + +- name: Verify idempotency of removal with list partially removed test result - {{ method }} + assert: + that: + - double_removal_result is not changed + msg: "state=absent shall not do anything when flatpak is not present" diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak_remote/aliases b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/aliases new file mode 100644 index 000000000..e462ed8cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/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/3 +destructive +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak_remote/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/meta/main.yml new file mode 100644 index 000000000..0ac87654d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/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_flatpak_remote diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/check_mode.yml b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/check_mode.yml new file mode 100644 index 000000000..86db5bf56 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/check_mode.yml @@ -0,0 +1,206 @@ +--- +# 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 + +# - Tests with absent flatpak remote ------------------------------------------- + +# state=present + +- name: Test addition of absent flatpak remote (check mode) + flatpak_remote: + name: flatpak-test + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + register: addition_result + check_mode: true + +- name: Verify addition of absent flatpak remote test result (check mode) + assert: + that: + - addition_result is changed + msg: "Adding an absent flatpak remote shall mark module execution as changed" + +- name: Test non-existent idempotency of addition of absent flatpak remote (check mode) + flatpak_remote: + name: flatpak-test + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + register: double_addition_result + check_mode: true + +- name: > + Verify non-existent idempotency of addition of absent flatpak remote + test result (check mode) + assert: + that: + - double_addition_result is changed + msg: | + Adding an absent flatpak remote a second time shall still mark module execution + as changed in check mode + +# state=absent + +- name: Test removal of absent flatpak remote not doing anything in check mode + flatpak_remote: + name: flatpak-test + state: absent + register: removal_result + check_mode: true + +- name: Verify removal of absent flatpak remote test result (check mode) + assert: + that: + - removal_result is not changed + msg: "Removing an absent flatpak remote shall mark module execution as not changed" + + +# - Tests with present flatpak remote ------------------------------------------- + +# state=present + +- name: Test addition of present flatpak remote (check mode) + flatpak_remote: + name: check-mode-test-remote + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + register: addition_result + check_mode: true + +- name: Verify addition of present flatpak remote test result (check mode) + assert: + that: + - addition_result is not changed + msg: "Adding a present flatpak remote shall mark module execution as not changed" + +# state=absent + +- name: Test removal of present flatpak remote not doing anything in check mode + flatpak_remote: + name: check-mode-test-remote + state: absent + register: removal_result + check_mode: true + +- name: Verify removal of present flatpak remote test result (check mode) + assert: + that: + - removal_result is changed + msg: "Removing a present flatpak remote shall mark module execution as changed" + +- name: Test non-existent idempotency of removal of present flatpak remote (check mode) + flatpak_remote: + name: check-mode-test-remote + state: absent + register: double_removal_result + check_mode: true + +- name: > + Verify non-existent idempotency of removal of present flatpak remote + test result (check mode) + assert: + that: + - double_removal_result is changed + msg: | + Removing a present flatpak remote a second time shall still mark module execution + as changed in check mode + + +# - Tests with disabled flatpak remote ------------------------------------------ + +# enabled=true + +- name: Test activation of disabled flatpak remote (check mode) + flatpak_remote: + name: check-mode-disabled-test-remote + enabled: true + register: activation_result + check_mode: true + +- name: Verify activation of disabled flatpak remote test result (check mode) + assert: + that: + - activation_result is changed + msg: "Enabling an disabled flatpak remote shall mark module execution as changed" + +- name: Test non-existent idempotency of activation of disabled flatpak remote (check mode) + flatpak_remote: + name: check-mode-disabled-test-remote + enabled: true + register: double_activation_result + check_mode: true + +- name: > + Verify non-existent idempotency of activation of disabled flatpak remote + test result (check mode) + assert: + that: + - double_activation_result is changed + msg: | + Enabling an disabled flatpak remote a second time shall still mark module execution + as changed in check mode + +# enabled=false + +- name: Test deactivation of disabled flatpak remote not doing anything in check mode + flatpak_remote: + name: check-mode-disabled-test-remote + enabled: false + register: deactivation_result + check_mode: true + +- name: Verify deactivation of disabled flatpak remote test result (check mode) + assert: + that: + - deactivation_result is not changed + msg: "Disabling an disabled flatpak remote shall mark module execution as not changed" + + +# - Tests with enabled flatpak remote ------------------------------------------ + +# enabled=true + +- name: Test activation of enabled flatpak remote (check mode) + flatpak_remote: + name: check-mode-enabled-test-remote + enabled: true + register: activation_result + check_mode: true + +- name: Verify activation of enabled flatpak remote test result (check mode) + assert: + that: + - activation_result is not changed + msg: "Enabling a enabled flatpak remote shall mark module execution as not changed" + +# enabled=false + +- name: Test deactivation of enabled flatpak remote not doing anything in check mode + flatpak_remote: + name: check-mode-enabled-test-remote + enabled: false + register: deactivation_result + check_mode: true + +- name: Verify deactivation of enabled flatpak remote test result (check mode) + assert: + that: + - deactivation_result is changed + msg: "Disabling a enabled flatpak remote shall mark module execution as changed" + +- name: Test non-existent idempotency of deactivation of enabled flatpak remote (check mode) + flatpak_remote: + name: check-mode-enabled-test-remote + enabled: false + register: double_deactivation_result + check_mode: true + +- name: > + Verify non-existent idempotency of deactivation of enabled flatpak remote + test result (check mode) + assert: + that: + - double_deactivation_result is changed + msg: | + "Disabling a enabled flatpak remote a second time shall still mark module execution + as changed in check mode diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/main.yml new file mode 100644 index 000000000..1c5091232 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/main.yml @@ -0,0 +1,50 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2018, Alexander Bethke +# Copyright (c) 2018, 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: + + - import_tasks: setup.yml + become: true + + # executable override + + - name: Test executable override + flatpak_remote: + name: irrelevant + remote: irrelevant + state: present + executable: nothing-that-exists + ignore_errors: true + register: executable_override_result + + - name: Verify executable override test result + assert: + that: + - executable_override_result is failed + - executable_override_result is not changed + msg: "Specifying non-existing executable shall fail module execution" + + - import_tasks: check_mode.yml + become: false + + - import_tasks: test.yml + become: false + vars: + method: user + + - import_tasks: test.yml + become: true + vars: + method: system + + when: | + ansible_distribution == 'Fedora' or + ansible_distribution == 'Ubuntu' and not ansible_distribution_major_version | int < 16 diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/setup.yml new file mode 100644 index 000000000..55a14c972 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/setup.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: Install flatpak on Fedora + dnf: + name: flatpak + state: present + when: ansible_distribution == 'Fedora' +- block: + - name: Activate flatpak ppa on Ubuntu versions older than 18.04/bionic + apt_repository: + repo: ppa:alexlarsson/flatpak + state: present + mode: '0644' + when: ansible_lsb.major_release | int < 18 + - name: Install flatpak package on Ubuntu + apt: + name: flatpak + state: present + when: ansible_distribution == 'Ubuntu' +- name: Install flatpak remote for testing check mode + flatpak_remote: + name: check-mode-test-remote + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + enabled: true +- name: Install disabled flatpak remote for testing check mode + flatpak_remote: + name: check-mode-disabled-test-remote + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + enabled: false +- name: Install enabled flatpak remote for testing check mode + flatpak_remote: + name: check-mode-enabled-test-remote + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + enabled: true diff --git a/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/test.yml new file mode 100644 index 000000000..e847205ff --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/flatpak_remote/tasks/test.yml @@ -0,0 +1,135 @@ +--- +# 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 + +# state=present + +- name: Test addition - {{ method }} + flatpak_remote: + name: flatpak-test + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + method: "{{ method }}" + register: addition_result + +- name: Verify addition test result - {{ method }} + assert: + that: + - addition_result is changed + msg: "state=present shall add flatpak when absent" + +- name: Test idempotency of addition - {{ method }} + flatpak_remote: + name: flatpak-test + flatpakrepo_url: /tmp/flatpak/repo/dummy-repo.flatpakrepo + state: present + method: "{{ method }}" + register: double_addition_result + +- name: Verify idempotency of addition test result - {{ method }} + assert: + that: + - double_addition_result is not changed + msg: "state=present shall not do anything when flatpak is already present" + +- name: Test updating remote url does not do anything - {{ method }} + flatpak_remote: + name: flatpak-test + flatpakrepo_url: https://a.different/repo.flatpakrepo + state: present + method: "{{ method }}" + register: url_update_result + +- name: Verify updating remote url does not do anything - {{ method }} + assert: + that: + - url_update_result is not changed + msg: "Trying to update the URL of an existing flatpak remote shall not do anything" + + +# enabled=false + +- name: Test deactivation - {{ method }} + flatpak_remote: + name: flatpak-test + enabled: false + method: "{{ method }}" + register: deactivation_result + +- name: Verify deactivation test result - {{ method }} + assert: + that: + - deactivation_result is changed + msg: "enable=false shall disable flatpak remote when enabled" + +- name: Test idempotency of deactivation - {{ method }} + flatpak_remote: + name: flatpak-test + enabled: false + method: "{{ method }}" + register: double_deactivation_result + +- name: Verify idempotency of deactivation test result - {{ method }} + assert: + that: + - double_deactivation_result is not changed + msg: "enabled=false shall not do anything when flatpak remote is already disabled" + + +# enabled=false + +- name: Test activation - {{ method }} + flatpak_remote: + name: flatpak-test + enabled: true + method: "{{ method }}" + register: activation_result + +- name: Verify activation test result - {{ method }} + assert: + that: + - activation_result is changed + msg: "enable=true shall enable flatpak remote when disabled" + +- name: Test idempotency of activation - {{ method }} + flatpak_remote: + name: flatpak-test + enabled: true + method: "{{ method }}" + register: double_activation_result + +- name: Verify idempotency of activation test result - {{ method }} + assert: + that: + - double_activation_result is not changed + msg: "enabled=true shall not do anything when flatpak remote is already enabled" + + +# state=absent + +- name: Test removal - {{ method }} + flatpak_remote: + name: flatpak-test + state: absent + method: "{{ method }}" + register: removal_result + +- name: Verify removal test result - {{ method }} + assert: + that: + - removal_result is changed + msg: "state=absent shall remove flatpak when present" + +- name: Test idempotency of removal - {{ method }} + flatpak_remote: + name: flatpak-test + state: absent + method: "{{ method }}" + register: double_removal_result + +- name: Verify idempotency of removal test result - {{ method }} + assert: + that: + - double_removal_result is not changed + msg: "state=absent shall not do anything when flatpak is not present" diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/aliases b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/aliases new file mode 100644 index 000000000..f69a127f4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/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 + +cloud/gandi +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/defaults/main.yml new file mode 100644 index 000000000..ec1808d8b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/defaults/main.yml @@ -0,0 +1,37 @@ +--- +# Copyright (c) 2020 Gregory Thiemonge +# 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 + +gandi_livedns_domain_name: "ansible-tests.org" +gandi_livedns_record_items: + +# Single A record +- record: test-www + type: A + values: + - 10.10.10.10 + ttl: 400 + update_values: + - 10.10.10.11 + update_ttl: 800 + +# Multiple A records +- record: test-www-multiple + type: A + ttl: 3600 + values: + - 10.10.11.10 + - 10.10.11.10 + update_values: + - 10.10.11.11 + - 10.10.11.13 + +# CNAME +- record: test-cname + type: CNAME + ttl: 10800 + values: + - test-www2 + update_values: + - test-www diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml new file mode 100644 index 000000000..c3f1c1798 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml @@ -0,0 +1,69 @@ +--- +# Copyright (c) 2020 Gregory Thiemonge +# 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 absent dns record + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + type: "{{ item.type }}" + ttl: "{{ item.ttl }}" + state: absent + register: result +- name: verify test absent dns record + assert: + that: + - result is successful + +- name: test create a dns record in check mode + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item['values'] }}" + ttl: "{{ item.ttl }}" + type: "{{ item.type }}" + check_mode: true + register: result +- name: verify test create a dns record in check mode + assert: + that: + - result is changed + +- name: test create a dns record + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item['values'] }}" + ttl: "{{ item.ttl }}" + type: "{{ item.type }}" + register: result +- name: verify test create a dns record + assert: + that: + - result is changed + - result.record['values'] == {{ item['values'] }} + - result.record.record == "{{ item.record }}" + - result.record.type == "{{ item.type }}" + - result.record.ttl == {{ item.ttl }} + +- name: test create a dns record idempotence + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item['values'] }}" + ttl: "{{ item.ttl }}" + type: "{{ item.type }}" + register: result +- name: verify test create a dns record idempotence + assert: + that: + - result is not changed + - result.record['values'] == {{ item['values'] }} + - result.record.record == "{{ item.record }}" + - result.record.type == "{{ item.type }}" + - result.record.ttl == {{ item.ttl }} diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/main.yml new file mode 100644 index 000000000..19ba4d8fb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/main.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) 2020 Gregory Thiemonge +# 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: record.yml + with_items: "{{ gandi_livedns_record_items }}" diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/record.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/record.yml new file mode 100644 index 000000000..d36e2e857 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/record.yml @@ -0,0 +1,8 @@ +--- +# Copyright (c) 2020 Gregory Thiemonge +# 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: create_record.yml +- include_tasks: update_record.yml +- include_tasks: remove_record.yml diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/remove_record.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/remove_record.yml new file mode 100644 index 000000000..c4b937fd5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/remove_record.yml @@ -0,0 +1,61 @@ +--- +# Copyright (c) 2020 Gregory Thiemonge +# 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 remove a dns record in check mode + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item.update_values | default(item['values']) }}" + type: "{{ item.type }}" + state: absent + check_mode: true + register: result +- name: verify test remove a dns record in check mode + assert: + that: + - result is changed + +- name: test remove a dns record + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item.update_values | default(item['values']) }}" + type: "{{ item.type }}" + state: absent + register: result +- name: verify test remove a dns record + assert: + that: + - result is changed + +- name: test remove a dns record idempotence + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item.update_values | default(item['values']) }}" + type: "{{ item.type }}" + state: absent + register: result +- name: verify test remove a dns record idempotence + assert: + that: + - result is not changed + +- name: test remove second dns record idempotence + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item['values'] }}" + type: "{{ item.type }}" + state: absent + register: result +- name: verify test remove a dns record idempotence + assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml new file mode 100644 index 000000000..a080560a7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml @@ -0,0 +1,59 @@ +--- +# Copyright (c) 2020 Gregory Thiemonge +# 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 update or add another dns record in check mode + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item.update_values | default(item['values']) }}" + ttl: "{{ item.update_ttl | default(item.ttl) }}" + type: "{{ item.type }}" + check_mode: true + register: result +- name: verify test update in check mode + assert: + that: + - result is changed + - result.record['values'] == {{ item.update_values | default(item['values']) }} + - result.record.record == "{{ item.record }}" + - result.record.type == "{{ item.type }}" + - result.record.ttl == {{ item.update_ttl | default(item.ttl) }} + +- name: test update or add another dns record + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item.update_values | default(item['values']) }}" + ttl: "{{ item.update_ttl | default(item.ttl) }}" + type: "{{ item.type }}" + register: result +- name: verify test update a dns record + assert: + that: + - result is changed + - result.record['values'] == {{ item.update_values | default(item['values']) }} + - result.record.record == "{{ item.record }}" + - result.record.ttl == {{ item.update_ttl | default(item.ttl) }} + - result.record.type == "{{ item.type }}" + +- name: test update or add another dns record idempotence + community.general.gandi_livedns: + api_key: "{{ gandi_api_key }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item.update_values | default(item['values']) }}" + ttl: "{{ item.update_ttl | default(item.ttl) }}" + type: "{{ item.type }}" + register: result +- name: verify test update a dns record idempotence + assert: + that: + - result is not changed + - result.record['values'] == {{ item.update_values | default(item['values']) }} + - result.record.record == "{{ item.record }}" + - result.record.ttl == {{ item.update_ttl | default(item.ttl) }} + - result.record.type == "{{ item.type }}" diff --git a/ansible_collections/community/general/tests/integration/targets/gem/aliases b/ansible_collections/community/general/tests/integration/targets/gem/aliases new file mode 100644 index 000000000..007bed538 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gem/aliases @@ -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 + +azp/posix/1 +destructive +skip/aix +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/gem/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/gem/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gem/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/gem/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml new file mode 100644 index 000000000..362c126bf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml @@ -0,0 +1,213 @@ +--- +#################################################################### +# 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 gem module +# Copyright (c) 2014, James Tanner +# 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 + +- when: + - not (ansible_os_family == 'Alpine') # TODO + block: + + - include_vars: '{{ item }}' + with_first_found: + - files: + - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml' + - '{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml' + - '{{ ansible_os_family }}.yml' + - 'default.yml' + paths: '../vars' + + - name: Install dependencies for test + package: + name: "{{ item }}" + state: present + loop: "{{ test_packages }}" + when: ansible_distribution != "MacOSX" + + - name: Install a gem + gem: + name: gist + state: present + register: install_gem_result + ignore_errors: true + + # when running as root on Fedora, '--install-dir' is set in the os defaults which is + # incompatible with '--user-install', we ignore this error for this case only + - name: fail if failed to install gem + fail: + msg: "failed to install gem: {{ install_gem_result.msg }}" + when: + - install_gem_result is failed + - not (ansible_user_uid == 0 and "User --install-dir or --user-install but not both" not in install_gem_result.msg) + + - block: + - name: List gems + command: gem list + register: current_gems + + - name: Ensure gem was installed + assert: + that: + - install_gem_result is changed + - current_gems.stdout is search('gist\s+\([0-9.]+\)') + + - name: Remove a gem + gem: + name: gist + state: absent + register: remove_gem_results + + - name: List gems + command: gem list + register: current_gems + + - name: Verify gem is not installed + assert: + that: + - remove_gem_results is changed + - current_gems.stdout is not search('gist\s+\([0-9.]+\)') + when: not install_gem_result is failed + + # install gem in --no-user-install + - block: + - name: Install a gem with --no-user-install + gem: + name: gist + state: present + user_install: false + register: install_gem_result + + - name: List gems + command: gem list + register: current_gems + + - name: Ensure gem was installed + assert: + that: + - install_gem_result is changed + - current_gems.stdout is search('gist\s+\([0-9.]+\)') + + - name: Remove a gem + gem: + name: gist + state: absent + register: remove_gem_results + + - name: List gems + command: gem list + register: current_gems + + - name: Verify gem is not installed + assert: + that: + - remove_gem_results is changed + - current_gems.stdout is not search('gist\s+\([0-9.]+\)') + when: ansible_user_uid == 0 + + # Check cutom gem directory + - name: Install gem in a custom directory with incorrect options + gem: + name: gist + state: present + install_dir: "{{ remote_tmp_dir }}/gems" + ignore_errors: true + register: install_gem_fail_result + + - debug: + var: install_gem_fail_result + tags: debug + + - name: Ensure previous task failed + assert: + that: + - install_gem_fail_result is failed + - install_gem_fail_result.msg == 'install_dir requires user_install=false' + + - name: Install a gem in a custom directory + gem: + name: gist + state: present + user_install: false + install_dir: "{{ remote_tmp_dir }}/gems" + register: install_gem_result + + - name: Find gems in custom directory + find: + paths: "{{ remote_tmp_dir }}/gems/gems" + file_type: directory + contains: gist + register: gem_search + + - name: Ensure gem was installed in custom directory + assert: + that: + - install_gem_result is changed + - gem_search.files[0].path is search('gist-[0-9.]+') + ignore_errors: true + + - name: Remove a gem in a custom directory + gem: + name: gist + state: absent + user_install: false + install_dir: "{{ remote_tmp_dir }}/gems" + register: install_gem_result + + - name: Find gems in custom directory + find: + paths: "{{ remote_tmp_dir }}/gems/gems" + file_type: directory + contains: gist + register: gem_search + + - name: Ensure gem was removed in custom directory + assert: + that: + - install_gem_result is changed + - gem_search.files | length == 0 + + # Custom directory for executables (--bindir) + - name: Install gem with custom bindir + gem: + name: gist + state: present + bindir: "{{ remote_tmp_dir }}/custom_bindir" + norc: true + user_install: false # Avoid conflicts between --install-dir and --user-install when running as root on CentOS / Fedora / RHEL + register: install_gem_result + + - name: Get stats of gem executable + stat: + path: "{{ remote_tmp_dir }}/custom_bindir/gist" + register: gem_bindir_stat + + - name: Ensure gem executable was installed in custom directory + assert: + that: + - install_gem_result is changed + - gem_bindir_stat.stat.exists and gem_bindir_stat.stat.isreg + + - name: Remove gem with custom bindir + gem: + name: gist + state: absent + bindir: "{{ remote_tmp_dir }}/custom_bindir" + norc: true + user_install: false # Avoid conflicts between --install-dir and --user-install when running as root on CentOS / Fedora / RHEL + register: install_gem_result + + - name: Get stats of gem executable + stat: + path: "{{ remote_tmp_dir }}/custom_bindir/gist" + register: gem_bindir_stat + + - name: Ensure gem executable was removed from custom directory + assert: + that: + - install_gem_result is changed + - not gem_bindir_stat.stat.exists diff --git a/ansible_collections/community/general/tests/integration/targets/gem/vars/FreeBSD.yml b/ansible_collections/community/general/tests/integration/targets/gem/vars/FreeBSD.yml new file mode 100644 index 000000000..b9d9cc2c2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gem/vars/FreeBSD.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 + +test_packages: + - "devel/ruby-gems" + - "ruby" diff --git a/ansible_collections/community/general/tests/integration/targets/gem/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/gem/vars/RedHat.yml new file mode 100644 index 000000000..2bb724bfe --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gem/vars/RedHat.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 + +test_packages: + - "rubygems" diff --git a/ansible_collections/community/general/tests/integration/targets/gem/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/gem/vars/default.yml new file mode 100644 index 000000000..b7496e12c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gem/vars/default.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 + +test_packages: [] diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/aliases b/ansible_collections/community/general/tests/integration/targets/git_config/aliases new file mode 100644 index 000000000..7b8c653de --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/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/files/gitconfig b/ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig new file mode 100644 index 000000000..92eeb7eb9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig @@ -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 + +[http] + proxy = foo diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/git_config/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/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/tasks/exclusion_state_list-all.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/exclusion_state_list-all.yml new file mode 100644 index 000000000..e294a83fb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/exclusion_state_list-all.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 + +- import_tasks: setup_no_value.yml + +- name: testing exclusion between state and list_all parameters + git_config: + list_all: true + state: absent + register: result + ignore_errors: true + +- name: assert git_config failed + assert: + that: + - result is failed + - "result.msg == 'parameters are mutually exclusive: list_all|state'" +... diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_no_state.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_no_state.yml new file mode 100644 index 000000000..4e41bf4e9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_no_state.yml @@ -0,0 +1,29 @@ +--- +# 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 without state + git_config: + name: "{{ option_name }}" + value: "{{ option_value }}" + scope: "{{ option_scope }}" + register: set_result + +- name: getting value without state + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert set changed and value is correct + assert: + that: + - set_result is changed + - set_result.diff.before == "\n" + - set_result.diff.after == option_value + "\n" + - get_result is not changed + - get_result.config_value == option_value +... diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present.yml new file mode 100644 index 000000000..cfc3bbe78 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present.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 + +- import_tasks: setup_no_value.yml + +- name: setting value with state=present + git_config: + name: "{{ option_name }}" + value: "{{ option_value }}" + scope: "{{ option_scope }}" + state: present + register: result + +- name: getting value with state=present + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: present + register: get_result + +- name: assert set changed and value is correct with state=present + assert: + that: + - set_result is changed + - set_result.diff.before == "\n" + - set_result.diff.after == option_value + "\n" + - get_result is not changed + - get_result.config_value == option_value +... 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 new file mode 100644 index 000000000..a61ffcc68 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml @@ -0,0 +1,32 @@ +--- +# 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 with state=present + git_config: + name: "{{ option_name }}" + value: "{{ option_value }}" + scope: "file" + file: "{{ remote_tmp_dir }}/gitconfig_file" + state: present + register: result + +- name: getting value with state=present + git_config: + name: "{{ option_name }}" + scope: "file" + file: "{{ remote_tmp_dir }}/gitconfig_file" + state: present + register: get_result + +- name: assert set changed and value is correct with state=present + assert: + that: + - set_result is changed + - set_result.diff.before == "\n" + - set_result.diff.after == option_value + "\n" + - get_result is not changed + - get_result.config_value == option_value 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 new file mode 100644 index 000000000..4dc72824c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml @@ -0,0 +1,35 @@ +--- +#################################################################### +# 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 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: + # testing parameters exclusion: state and list_all + - import_tasks: exclusion_state_list-all.yml + # testing get/set option without state + - import_tasks: get_set_no_state.yml + # testing get/set option with state=present + - import_tasks: get_set_state_present.yml + # testing get/set option with state=present and scope=file + - import_tasks: get_set_state_present_file.yml + # testing state=absent without value to delete + - import_tasks: unset_no_value.yml + # testing state=absent with value to delete + - import_tasks: unset_value.yml + # testing state=absent with value to delete and a defined value parameter + - import_tasks: precedence_between_unset_and_value.yml + # testing state=absent with check mode + - import_tasks: unset_check_mode.yml + # testing for case in issue #1776 + - import_tasks: set_value_with_tilde.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/precedence_between_unset_and_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml new file mode 100644 index 000000000..a76fbab9c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/precedence_between_unset_and_value.yml @@ -0,0 +1,29 @@ +--- +# 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 value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + value: bar + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unset changed and deleted value + assert: + that: + - unset_result is changed + - unset_result.diff.before == option_value + "\n" + - unset_result.diff.after == "\n" + - get_result.config_value == '' +... 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 new file mode 100644 index 000000000..f78e709bd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml @@ -0,0 +1,37 @@ +--- +# 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.hooksPath + value: '~/foo/bar' + state: present + scope: global + register: set_result + +- name: setting value again + git_config: + name: core.hooksPath + value: '~/foo/bar' + state: present + scope: global + register: set_result2 + +- name: getting value + git_config: + name: core.hooksPath + scope: global + register: get_result + +- name: assert set changed and value is correct + assert: + that: + - set_result 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/setup.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup.yml new file mode 100644 index 000000000..6e5516da5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/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/tasks/setup_no_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_no_value.yml new file mode 100644 index 000000000..8e12c350c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_no_value.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 : deleting gitconfig file +- name: set up without value + file: + path: ~/.gitconfig + state: absent + +- name: set up without value (file) + file: + path: "{{ remote_tmp_dir }}/gitconfig_file" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_value.yml new file mode 100644 index 000000000..126b1ae4b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/setup_value.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: set up with value + copy: + src: gitconfig + dest: ~/.gitconfig + +- name: set up with value (file) + copy: + src: gitconfig + dest: "{{ remote_tmp_dir }}/gitconfig_file" diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_check_mode.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_check_mode.yml new file mode 100644 index 000000000..39bce3379 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_check_mode.yml @@ -0,0 +1,29 @@ +--- +# 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 value with check mode + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + check_mode: true + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unset changed but dit not delete value + assert: + that: + - unset_result is changed + - unset_result.diff.before == option_value + "\n" + - unset_result.diff.after == "\n" + - get_result.config_value == option_value +... diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_no_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_no_value.yml new file mode 100644 index 000000000..394276cad --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_no_value.yml @@ -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 + +- import_tasks: setup_no_value.yml + +- name: unsetting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unsetting didn't change + assert: + that: + - unset_result is not changed + - unset_result.msg == 'no setting to unset' + - get_result.config_value == '' +... diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_value.yml new file mode 100644 index 000000000..dfa535a2d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_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 value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + state: absent + register: unset_result + +- name: getting value + git_config: + name: "{{ option_name }}" + scope: "{{ option_scope }}" + register: get_result + +- name: assert unset changed and deleted value + assert: + that: + - unset_result is changed + - unset_result.diff.before == option_value + "\n" + - unset_result.diff.after == "\n" + - get_result.config_value == '' +... diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/git_config/vars/main.yml new file mode 100644 index 000000000..3cca3ef6e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/git_config/vars/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 + +git_version_supporting_includes: 1.7.10 +option_name: http.proxy +option_value: 'foo' +option_scope: global +... diff --git a/ansible_collections/community/general/tests/integration/targets/github_issue/aliases b/ansible_collections/community/general/tests/integration/targets/github_issue/aliases new file mode 100644 index 000000000..428e8289d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/github_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 + +azp/posix/1 +destructive diff --git a/ansible_collections/community/general/tests/integration/targets/github_issue/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/github_issue/tasks/main.yml new file mode 100644 index 000000000..a7e43c171 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/github_issue/tasks/main.yml @@ -0,0 +1,38 @@ +#################################################################### +# 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 github_issue module. +# +# Copyright (c) 2017-2018, Abhijeet Kasurde +# 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: Check if GitHub issue is closed or not + github_issue: + organization: "{{ organization }}" + repo: "{{ repo }}" + issue: "{{ issue }}" + action: get_status + register: get_status_0002 + +- assert: + that: + - get_status_0002 is changed + - get_status_0002.issue_status == 'closed' + +- name: Check if GitHub issue is closed or not + github_issue: + organization: "{{ organization }}" + repo: "{{ repo }}" + issue: "{{ non_existent_issue }}" + action: get_status + register: get_status_0003 + ignore_errors: true + +- assert: + that: + - get_status_0003 is not changed + - get_status_0003 is failed + - "'Failed' in get_status_0003.msg" diff --git a/ansible_collections/community/general/tests/integration/targets/github_issue/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/github_issue/vars/main.yml new file mode 100644 index 000000000..8b2a2de6e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/github_issue/vars/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 + +issue: 23642 +non_existent_issue: 1111111 +organization: ansible +repo: ansible diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_branch/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_branch/aliases new file mode 100644 index 000000000..d163e8d9c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_branch/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/1 +disabled diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_branch/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_branch/defaults/main.yml new file mode 100644 index 000000000..a5f0a0751 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_branch/defaults/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 + +gitlab_branch: ansible_test_branch +gitlab_project_name: ansible_test_project diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_branch/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_branch/tasks/main.yml new file mode 100644 index 000000000..19d90e15c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_branch/tasks/main.yml @@ -0,0 +1,69 @@ +--- +#################################################################### +# 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: Create {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + initialize_with_readme: true + state: present + +- name: Create branch {{ gitlab_branch }} + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "{{ gitlab_project_name }}" + branch: "{{ gitlab_branch }}" + ref_branch: main + state: present + +- name: Create branch {{ gitlab_branch }} ( Idempotency test ) + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "{{ gitlab_project_name }}" + branch: "{{ gitlab_branch }}" + ref_branch: main + state: present + register: create_branch + +- name: Test module is idempotent + assert: + that: + - create_branch is not changed + +- name: Cleanup branch {{ gitlab_branch }} + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "{{ gitlab_project_name }}" + branch: "{{ gitlab_branch }}" + state: absent + register: delete_branch + +- name: Test module is idempotent + assert: + that: + - delete_branch is changed + +- name: Clean up {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/aliases new file mode 100644 index 000000000..fc0e157c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/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_deploy_key/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/defaults/main.yml new file mode 100644 index 000000000..8225571b6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/defaults/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 + +gitlab_project_name: ansible_test_project +gitlab_deploy_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJnTYY7CYk1F/wBklpdRxudxN6KeXgfhutkiCigSfPhe ansible_test" +gitlab_deploy_key_new: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDL1TDkIY2uu6NYRD0G5qGeHTd/AoqQpCw1XENXDnTLDN5DNZVCO1+7xfA5DR5V2tcR691Q005BKxoCo+uUBd1aAM7JWyuXl050rZCXBj4oaUF7urjDANQ7FzYuvqp9h8NGkvzfBYz5YBfu4vh43ajnF0daSyZy4RlxeG9G44vnHElXTQ0igaOCSta/23FdERIYzKxuX4Ul42AwtSmCRwbkN4fC86o0UwW2q0zkgFOUoojtS/Avh0aX8UQyeagaPJFXCc/ldG1mMK020GQAEa8aQcUpysnEzZdq6no5Zyn/WQSobpnJ9CraHhdb1QQytg/+c+CgjSN0cERhTvLn0WsQ043jo5g1kSHNu+OiYXmVwTxe95nXCsoYmCNF/DmezjYVxe9BGlKRAEuHsNi87Il84nBnzKVHGlkq8eJNTR8ASjNkjI7pGS0zxCDB55c3LHh4Aa1xU+nwINRurn/TEDpDZc43/XOnt+aqbxkeWbMtOD/r2gfMj8lNZJ/IyamWy7HcFgGpTZJln4WxVLF+Cz56qa8Hf9WzJL+8Lq7eE3sJKOagn/zPgqeybXbTIPSr3fshq3yE8FYHpFKS4aLvQC/XSLCywrhr25DKBn9UHIZmgC9hxMnVJCKux+ltwGJOKIaoj+5n3+DvM+E3fK3fkADo5+Frzay6/rLTwKWUrzfjQQ== ansible_test_new" diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/tasks/main.yml new file mode 100644 index 000000000..c345c2467 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_deploy_key/tasks/main.yml @@ -0,0 +1,78 @@ +--- +#################################################################### +# 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: Create {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: present + +- name: Cleanup deploy key to {{ gitlab_project_name }} + gitlab_deploy_key: + login_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + server_url: "{{ gitlab_host }}" + title: "{{ gitlab_project_name }}" + key: "{{ gitlab_deploy_key }}" + state: absent + + +- name: Add deploy key to {{ gitlab_project_name }} + gitlab_deploy_key: + login_token: "{{ gitlab_login_token }}" + project: "root/{{ gitlab_project_name }}" + server_url: "{{ gitlab_host }}" + title: "{{ gitlab_project_name }}" + key: "{{ gitlab_deploy_key }}" + state: present + register: deploy_key_status + +- assert: + that: + - deploy_key_status is changed + - deploy_key_status.deploy_key.key == gitlab_deploy_key + + +- name: Update public key {{ gitlab_project_name }} (change expected) + gitlab_deploy_key: + login_token: "{{ gitlab_login_token }}" + project: "root/{{ gitlab_project_name }}" + server_url: "{{ gitlab_host }}" + title: "{{ gitlab_project_name }}" + key: "{{ gitlab_deploy_key_new }}" + state: present + register: deploy_key_status + +- assert: + that: + - deploy_key_status is changed + - deploy_key_status.deploy_key.key == gitlab_deploy_key_new + +- name: Update public key {{ gitlab_project_name }} (no change expected) + gitlab_deploy_key: + login_token: "{{ gitlab_login_token }}" + project: "root/{{ gitlab_project_name }}" + server_url: "{{ gitlab_host }}" + title: "{{ gitlab_project_name }}" + key: "{{ gitlab_deploy_key_new }}" + state: present + register: deploy_key_status + +- assert: + that: + - not deploy_key_status.changed + - deploy_key_status.deploy_key.key == gitlab_deploy_key_new diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_group/aliases new file mode 100644 index 000000000..fc0e157c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group/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/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group/defaults/main.yml new file mode 100644 index 000000000..01863abe3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group/defaults/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 + +gitlab_group: ansible_test_project diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group/tasks/main.yml new file mode 100644 index 000000000..a0355094f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group/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 + +- name: Cleanup GitLab Group + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + state: absent + +- name: Create GitLab Group + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + state: present + register: gitlab_group_state + +- name: Test group created + assert: + that: + - gitlab_group_state is changed + + +- name: Create GitLab Group ( Idempotency test ) + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + state: present + register: gitlab_group_state_again + +- name: Test module is idempotent + assert: + that: + - gitlab_group_state_again is not changed + +- name: Cleanup GitLab Group for Description Test + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + state: absent + +- name: Create GitLab Group for Description Test + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + description: My Test Group + state: present + register: gitlab_group_state_desc + +- name: Test group created with Description + assert: + that: + - gitlab_group_state_desc.group.description == "My Test Group" + +- name: Cleanup GitLab Group for project_creation_level Test + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + state: absent + +- name: Create GitLab Group for project_creation_level Test + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + project_creation_level: noone + state: present + register: gitlab_group_state_pcl + +- name: Test group created with project_creation_level + assert: + that: + - gitlab_group_state_pcl.group.project_creation_level == "noone" + +- name: Cleanup GitLab Group for require_two_factor_authentication Test + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + state: absent + +- name: Create GitLab Group for project_creation_level Test + gitlab_group: + api_url: "{{ gitlab_host }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + name: ansible_test_group + path: ansible_test_group + require_two_factor_authentication: true + state: present + register: gitlab_group_state_rtfa + +- name: Test group created with project_creation_level + assert: + that: + - gitlab_group_state_rtfa.group.require_two_factor_authentication == true diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/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_group_members/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/tasks/main.yml new file mode 100644 index 000000000..aa75096da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/tasks/main.yml @@ -0,0 +1,74 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Test code for gitlab_group_members module +# +# Copyright (c) 2020, Zainab Alsaffar +# 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 library + pip: + name: python-gitlab + state: present + +- name: Add a User to A GitLab Group + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_user: '{{ username }}' + access_level: '{{ gitlab_access_level }}' + state: present + +- name: Remove a User from A GitLab Group + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_user: '{{ username }}' + state: absent + +- name: Add a list of Users to A GitLab Group + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_user: '{{ userlist }}' + access_level: '{{ gitlab_access_level }}' + state: present + +- name: Remove a list of Users to A GitLab Group + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_user: '{{ userlist }}' + state: absent + +- name: Add a list of Users with Dedicated Access Levels to A GitLab Group + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_users_access: '{{ dedicated_access_users }}' + state: present + +- name: Remove a list of Users with Dedicated Access Levels to A GitLab Group + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_users_access: '{{ dedicated_access_users }}' + state: absent + +- name: Add a user, remove all others which might be on this access level + gitlab_group_members: + api_url: '{{ gitlab_server_url }}' + api_token: '{{ gitlab_api_access_token }}' + gitlab_group: '{{ gitlab_group_name }}' + gitlab_user: '{{ username }}' + access_level: '{{ gitlab_access_level }}' + pruge_users: '{{ gitlab_access_level }}' + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/vars/main.yml new file mode 100644 index 000000000..908260418 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_members/vars/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_server_url: https://gitlabserver.example.com +gitlab_api_access_token: 126hngbscx890cv09b +gitlab_group_name: groupname1 +username: username1 +gitlab_access_level: developer +userlist: + - username1 + - username2 +dedicated_access_users: + - name: username1 + access_level: "developer" + - name: username2 + access_level: "maintainer" diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group_variable/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_group_variable/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_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_group_variable/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group_variable/tasks/main.yml new file mode 100644 index 000000000..39a3a5df8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_variable/tasks/main.yml @@ -0,0 +1,706 @@ +--- +#################################################################### +# 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_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + purge: true + +- name: add a variable value in check_mode + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: checkmode + check_mode: true + register: gitlab_group_variable_state + +- name: check_mode state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: apply add value from check_mode test + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + variables: + - name: ACCESS_KEY_ID + value: checkmode + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: test new format + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + register: gitlab_group_variable_state + +- name: state must be not changed + assert: + that: + - gitlab_group_variable_state is not changed + +- name: change protected attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + protected: true + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: revert protected attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + protected: false + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: change masked attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + masked: true + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: revert masked attribute by not mention it + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: revert again masked attribute by not mention it (idempotent) + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + register: gitlab_group_variable_state + +- name: state must be not changed + assert: + that: + - gitlab_group_variable_state is not changed + +- name: set both (masked and protected) attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + masked: true + protected: true + variable_type: env_var + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: set again both (masked and protected) attribute (idempotent) + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + masked: true + protected: true + variable_type: env_var + register: gitlab_group_variable_state + +- name: state must not be changed + assert: + that: + - gitlab_group_variable_state is not changed + +- name: revert both (masked and protected) attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + protected: false + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: change a variable value in check_mode again + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: checkmode + check_mode: true + register: gitlab_group_variable_state + +- name: check_mode state must not be changed + assert: + that: + - gitlab_group_variable_state is not changed + +- name: apply again the value change from check_mode test + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: checkmode + register: gitlab_group_variable_state + +- name: state must not be changed + assert: + that: + - gitlab_group_variable_state is not changed + +- name: change environment scope + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + environment_scope: testing + value: checkmode + register: gitlab_group_variable_state + when: gitlab_premium_tests is defined + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + when: gitlab_premium_tests is defined + +- name: apply again the environment scope change + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: + environment_scope: testing + value: checkmode + register: gitlab_group_variable_state + when: gitlab_premium_tests is defined + +- name: state must not be changed + assert: + that: + - gitlab_group_variable_state is not changed + when: gitlab_premium_tests is defined + +- name: purge all variables at the beginning + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + purge: true + +- name: set two test variables + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: abc123 + SECRET_ACCESS_KEY: 321cba + register: gitlab_group_variable_state + +- name: set two test variables state must be changed + assert: + that: + - gitlab_group_variable_state is changed + - gitlab_group_variable_state.group_variable.added|length == 2 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + +- name: re-set two test variables + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: abc123 + SECRET_ACCESS_KEY: 321cba + register: gitlab_group_variable_state + +- name: re-set two test variables state must not be changed + assert: + that: + - gitlab_group_variable_state is not changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 2 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + +- name: edit one variable + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: changed + purge: false + register: gitlab_group_variable_state + +- name: edit one variable state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 1 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 1 + - gitlab_group_variable_state.group_variable.updated[0] == "ACCESS_KEY_ID" + +- name: append one variable + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + some: value + purge: false + register: gitlab_group_variable_state + +- name: append one variable state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 1 + - gitlab_group_variable_state.group_variable.untouched|length == 2 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + - gitlab_group_variable_state.group_variable.added[0] == "some" + +- name: re-set all variables + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + ACCESS_KEY_ID: changed + SECRET_ACCESS_KEY: 321cba + some: value + register: gitlab_group_variable_state + +- name: re-set all variables state must not be changed + assert: + that: + - not gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 3 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + +- name: set one variables and purge all others + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + some: value + purge: true + register: gitlab_group_variable_state + +- name: set one variables and purge all others state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 1 + - gitlab_group_variable_state.group_variable.removed|length == 2 + - gitlab_group_variable_state.group_variable.updated|length == 0 + +- name: only one variable is left + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + some: value + purge: false + register: gitlab_group_variable_state + +- name: only one variable is left state must not be changed + assert: + that: + - not gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 1 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + - gitlab_group_variable_state.group_variable.untouched[0] == "some" + +- name: test integer values + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + some: 42 + purge: false + register: gitlab_group_variable_state + +- name: only one variable is left state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 1 + +- name: test float values + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + some: 42.23 + purge: false + register: gitlab_group_variable_state + +- name: only one variable is left state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 1 + +- name: delete the last left variable + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + state: absent + vars: + some: value + register: gitlab_group_variable_state + +- name: no variable is left state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 1 + - gitlab_group_variable_state.group_variable.updated|length == 0 + - gitlab_group_variable_state.group_variable.removed[0] == "some" + +- name: add one variable with variable_type file + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + variables: + - name: my_test_var + value: my_test_value + variable_type: file + purge: false + register: gitlab_group_variable_state + +- name: append one variable state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 1 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + - gitlab_group_variable_state.group_variable.added[0] == "my_test_var" + +- name: change variable_type attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + my_test_var: + value: my_test_value + variable_type: env_var + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: revert variable_type attribute + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + my_test_var: + value: my_test_value + variable_type: file + register: gitlab_group_variable_state + +- name: state must be changed + assert: + that: + - gitlab_group_variable_state is changed + +- name: delete the variable_type file variable + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + state: absent + vars: + my_test_var: my_test_value + register: gitlab_group_variable_state + +- name: no variable is left state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 1 + - gitlab_group_variable_state.group_variable.updated|length == 0 + - gitlab_group_variable_state.group_variable.removed[0] == "my_test_var" + +- name: set complete page and purge existing ones + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + page1_var01: value + page1_var02: value + page1_var03: value + page1_var04: value + page1_var05: value + page1_var06: value + page1_var07: value + page1_var08: value + page1_var09: value + page1_var10: value + page1_var11: value + page1_var12: value + page1_var13: value + page1_var14: value + page1_var15: value + page1_var16: value + page1_var17: value + page1_var18: value + page1_var19: value + page1_var20: value + purge: true + register: gitlab_group_variable_state + +- name: complete page added state must be changed + assert: + that: + - gitlab_group_variable_state is changed + - gitlab_group_variable_state.group_variable.added|length == 20 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + +- name: set complete page and keep existing ones + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + vars: + page2_var01: value + page2_var02: value + page2_var03: value + page2_var04: value + page2_var05: value + page2_var06: value + page2_var07: value + page2_var08: value + page2_var09: value + page2_var10: value + page2_var11: value + page2_var12: value + page2_var13: value + page2_var14: value + page2_var15: value + page2_var16: value + page2_var17: value + page2_var18: value + page2_var19: value + page2_var20: value + purge: false + register: gitlab_group_variable_state + +- name: existing page untouched state must be changed + assert: + that: + - gitlab_group_variable_state is changed + - gitlab_group_variable_state.group_variable.added|length == 20 + - gitlab_group_variable_state.group_variable.untouched|length == 20 + +- name: check that no variables are left + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + purge: true + register: gitlab_group_variable_state + +- name: check that no variables are untouched state must be changed + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 0 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 40 + - gitlab_group_variable_state.group_variable.updated|length == 0 + +- name: same vars, diff scope + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + purge: true + variables: + - name: SECRET_ACCESS_KEY + value: 3214cbad + masked: true + protected: true + variable_type: env_var + environment_scope: production + - name: SECRET_ACCESS_KEY + value: hello_world + masked: true + protected: true + variable_type: env_var + environment_scope: development + register: gitlab_group_variable_state + when: gitlab_premium_tests is defined + +- name: verify two vars + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.added|length == 2 + - gitlab_group_variable_state.group_variable.untouched|length == 0 + - gitlab_group_variable_state.group_variable.removed|length == 0 + - gitlab_group_variable_state.group_variable.updated|length == 0 + when: gitlab_premium_tests is defined + +- name: throw error when state is present but no value is given + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + variables: + - name: delete_me + register: gitlab_group_variable_state + ignore_errors: true + +- name: verify fail + assert: + that: + - gitlab_group_variable_state.failed + - gitlab_group_variable_state is not changed + +- name: set a new variable to delete it later + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + purge: true + variables: + - name: delete_me + value: ansible + register: gitlab_group_variable_state + +- name: verify the change + assert: + that: + - gitlab_group_variable_state.changed + +- name: delete variable without referencing its value + gitlab_group_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + group: "{{ gitlab_group_name }}" + state: absent + variables: + - name: delete_me + register: gitlab_group_variable_state + +- name: verify deletion + assert: + that: + - gitlab_group_variable_state.changed + - gitlab_group_variable_state.group_variable.removed|length == 1 diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_hook/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_hook/aliases new file mode 100644 index 000000000..fc0e157c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_hook/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_hook/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_hook/defaults/main.yml new file mode 100644 index 000000000..69cec7d33 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_hook/defaults/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 + +gitlab_project_name: ansible_test_project +gitlab_hook_url: http://gitlab.example.com/hook diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_hook/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_hook/tasks/main.yml new file mode 100644 index 000000000..aa06f6c81 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_hook/tasks/main.yml @@ -0,0 +1,77 @@ +--- +#################################################################### +# 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: Create {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: present + +- name: Cleanup GitLab hook + gitlab_hook: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + hook_url: "{{ gitlab_hook_url }}" + project: "{{ gitlab_project_name }}" + state: absent + +- name: Create GitLab Hook + gitlab_hook: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + hook_url: "{{ gitlab_hook_url }}" + project: "{{ gitlab_project_name }}" + state: present + register: gitlab_hook_state + +- name: Test group created + assert: + that: + - gitlab_hook_state is changed + + +- name: Create GitLab Hook ( Idempotency test ) + gitlab_hook: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + hook_url: "{{ gitlab_hook_url }}" + project: "{{ gitlab_project_name }}" + state: present + register: gitlab_hook_state_again + +- name: Test module is idempotent + assert: + that: + - gitlab_hook_state_again is not changed + +- name: Remove GitLab hook + gitlab_hook: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + hook_url: "{{ gitlab_hook_url }}" + project: "{{ gitlab_project_name }}" + state: absent + register: gitlab_hook_state_absent + +- name: Assert hook has been removed + assert: + that: + - gitlab_hook_state_absent is changed diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_project/aliases new file mode 100644 index 000000000..fc0e157c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project/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/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project/defaults/main.yml new file mode 100644 index 000000000..457129c22 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project/defaults/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 + +gitlab_project_name: ansible_test_project +gitlab_deploy_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJnTYY7CYk1F/wBklpdRxudxN6KeXgfhutkiCigSfPhe ansible_test" diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project/tasks/main.yml new file mode 100644 index 000000000..0a9e47188 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project/tasks/main.yml @@ -0,0 +1,49 @@ +--- +#################################################################### +# 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: Clean up {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: absent + +- name: Create {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + initialize_with_readme: true + state: present + register: gitlab_project_state + +- assert: + that: + - gitlab_project_state is changed + +- name: Create {{ gitlab_project_name }} (Test idempotency) + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: present + register: gitlab_project_state_again + +- assert: + that: + - gitlab_project_state_again is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/aliases new file mode 100644 index 000000000..9f72f3711 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ (lunik@tiwabbit.fr) +# 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_project_badge/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/defaults/main.yml new file mode 100644 index 000000000..bf84a4751 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/defaults/main.yml @@ -0,0 +1,11 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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: glpat-XXXXXXXXXXXXXXXXXXXX +gitlab_api_url: https://gitlab.com +gitlab_project_name: ansible_test_project +gitlab_badge_link_url: 'https://example.gitlab.com/%{project_path}' +updated_gitlab_badge_link_url: 'https://test.gitlab.com/%{project_path}' +gitlab_badge_image_url: 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg' \ No newline at end of file diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/tasks/main.yml new file mode 100644 index 000000000..efc090ef7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_badge/tasks/main.yml @@ -0,0 +1,214 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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: Create {{ gitlab_project_name }} + gitlab_project: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + name: "{{ gitlab_project_name }}" + initialize_with_readme: true + state: present + +- name: Create Badge (check) + check_mode: true + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_create_check_task + +- ansible.builtin.debug: + var: gitlab_badge_create_check_task + +- name: Check module call result + assert: + that: + - gitlab_badge_create_check_task.changed + - not gitlab_badge_create_check_task.failed + +- name: Create Badge + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_create_task + +- ansible.builtin.debug: + var: gitlab_badge_create_task + +- name: Check module call result + assert: + that: + - gitlab_badge_create_task.changed + - not gitlab_badge_create_task.failed + +- name: Create Badge (confirmation) + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_create_confirmation_task + +- ansible.builtin.debug: + var: gitlab_badge_create_confirmation_task + +- name: Check module call result + assert: + that: + - not gitlab_badge_create_confirmation_task.changed + - not gitlab_badge_create_confirmation_task.failed + +- name: Update Badge (check) + check_mode: true + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_update_check_task + +- ansible.builtin.debug: + var: gitlab_badge_update_check_task + +- name: Check module call result + assert: + that: + - gitlab_badge_update_check_task.changed + - not gitlab_badge_update_check_task.failed + +- name: Update Badge + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_update_task + +- ansible.builtin.debug: + var: gitlab_badge_update_task + +- name: Check module call result + assert: + that: + - gitlab_badge_update_task.changed + - not gitlab_badge_update_task.failed + +- name: Update Badge (confirmation) + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: present + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_update_confirmation_task + +- ansible.builtin.debug: + var: gitlab_badge_update_confirmation_task + +- name: Check module call result + assert: + that: + - not gitlab_badge_update_confirmation_task.changed + - not gitlab_badge_update_confirmation_task.failed + +- name: Delete Badge (check) + check_mode: true + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: absent + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_delete_check_task + +- ansible.builtin.debug: + var: gitlab_badge_delete_check_task + +- name: Check module call result + assert: + that: + - gitlab_badge_delete_check_task.changed + - not gitlab_badge_delete_check_task.failed + +- name: Delete Badge + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: absent + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_delete_task + +- ansible.builtin.debug: + var: gitlab_badge_delete_task + +- name: Check module call result + assert: + that: + - gitlab_badge_delete_task.changed + - not gitlab_badge_delete_task.failed + +- name: Delete Badge (confirmation) + gitlab_project_badge: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + project: "{{ gitlab_project_name }}" + state: absent + link_url: "{{ updated_gitlab_badge_link_url }}" + image_url: "{{ gitlab_badge_image_url }}" + register: gitlab_badge_delete_confirmation_task + +- ansible.builtin.debug: + var: gitlab_badge_delete_confirmation_task + +- name: Check module call result + assert: + that: + - not gitlab_badge_delete_confirmation_task.changed + - not gitlab_badge_delete_confirmation_task.failed + +- name: Clean up {{ gitlab_project_name }} + gitlab_project: + api_url: "{{ gitlab_api_url }}" + validate_certs: false + api_token: "{{ gitlab_api_token }}" + name: "{{ gitlab_project_name }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/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_project_members/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/defaults/main.yml new file mode 100644 index 000000000..72d5a68f1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/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_server_url: https://gitlab.com +gitlab_api_access_token: "token" +gitlab_project: some_project +username: some_user +gitlab_access_level: developer +userlist: + - username1 + - username2 +dedicated_access_users: + - name: username1 + access_level: "developer" + - name: username2 + access_level: "maintainer" diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/tasks/main.yml new file mode 100644 index 000000000..215abad44 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_members/tasks/main.yml @@ -0,0 +1,124 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Test code for gitlab_project_members module +# +# Copyright (c) 2021, Sergey Mikhaltsov +# 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 library + pip: + name: python-gitlab + state: present + +- name: Clean UP before tests + community.general.gitlab_project_members: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ username }}" + state: absent + +- name: Add a User to A GitLab Project + community.general.gitlab_project_members: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ username }}" + access_level: "{{ gitlab_access_level }}" + state: present + register: gitlab_project_members_state + +- name: Test member added to project + assert: + that: + - gitlab_project_members_state is changed + +- name: Add a User to A GitLab Project ( Idempotency test ) + community.general.gitlab_project_members: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ username }}" + access_level: "{{ gitlab_access_level }}" + state: present + register: gitlab_project_members_state_again + +- name: Test module is idempotent + assert: + that: + - gitlab_project_members_state_again is not changed + +- name: Remove a User from A GitLab Project + community.general.gitlab_project_members: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ username }}" + state: absent + register: remove_gitlab_project_members_state + +- name: Test member removed from project + assert: + that: + - remove_gitlab_project_members_state is changed + +- name: Remove a User from A GitLab Project ( Idempotency test ) + community.general.gitlab_project_members: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ username }}" + state: absent + register: remove_gitlab_project_members_state_again + +- name: Test module is idempotent + assert: + that: + - remove_gitlab_project_members_state_again is not changed + +- name: Add a list of Users to A GitLab Project + community.general.gitlab_project_members: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ userlist }}" + access_level: "{{ gitlab_access_level }}" + state: present + +- name: Remove a list of Users to A GitLab Project + community.general.gitlab_project_members:: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ userlist }}" + state: absent + +- name: Add a list of Users with Dedicated Access Levels to A GitLab Project + community.general.gitlab_project_members:: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_users_access: "{{ dedicated_access_users }}" + state: present + +- name: Remove a list of Users with Dedicated Access Levels to A GitLab Project + community.general.gitlab_project_members:: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_users_access: "{{ dedicated_access_users }}" + state: absent + +- name: Add a user, remove all others which might be on this access level + community.general.gitlab_project_members:: + api_url: "{{ gitlab_server_url }}" + api_token: "{{ gitlab_api_access_token }}" + project: "{{ gitlab_project }}" + gitlab_user: "{{ username }}" + access_level: "{{ gitlab_access_level }}" + pruge_users: "{{ gitlab_access_level }}" + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_variable/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_project_variable/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_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_project_variable/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_variable/tasks/main.yml new file mode 100644 index 000000000..0645da0fd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_variable/tasks/main.yml @@ -0,0 +1,701 @@ +--- +#################################################################### +# 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_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + purge: true + +- name: add a variable value in check_mode + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: checkmode + check_mode: true + register: gitlab_project_variable_state + +- name: check_mode state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: apply add value from check_mode test + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + variables: + - name: ACCESS_KEY_ID + value: checkmode + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: test new format + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + register: gitlab_project_variable_state + +- name: state must be not changed + assert: + that: + - gitlab_project_variable_state is not changed + +- name: change protected attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + protected: true + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: revert protected attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + protected: false + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: change masked attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + masked: true + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: revert masked attribute by not mention it + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: revert again masked attribute by not mention it (idempotent) + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + register: gitlab_project_variable_state + +- name: state must be not changed + assert: + that: + - gitlab_project_variable_state is not changed + +- name: set both (masked and protected) attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + masked: true + protected: true + variable_type: env_var + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: set again both (masked and protected) attribute (idempotent) + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + masked: true + protected: true + variable_type: env_var + register: gitlab_project_variable_state + +- name: state must not be changed + assert: + that: + - gitlab_project_variable_state is not changed + +- name: revert both (masked and protected) attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + value: checkmode + protected: false + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: change a variable value in check_mode again + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: checkmode + check_mode: true + register: gitlab_project_variable_state + +- name: check_mode state must not be changed + assert: + that: + - gitlab_project_variable_state is not changed + +- name: apply again the value change from check_mode test + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: checkmode + register: gitlab_project_variable_state + +- name: state must not be changed + assert: + that: + - gitlab_project_variable_state is not changed + +- name: change environment scope + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + environment_scope: testing + value: checkmode + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: apply again the environment scope change + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: + environment_scope: testing + value: checkmode + register: gitlab_project_variable_state + +- name: state must not be changed + assert: + that: + - gitlab_project_variable_state is not changed + +- name: purge all variables at the beginning + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + purge: true + +- name: set two test variables + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: abc123 + SECRET_ACCESS_KEY: 321cba + register: gitlab_project_variable_state + +- name: set two test variables state must be changed + assert: + that: + - gitlab_project_variable_state is changed + - gitlab_project_variable_state.project_variable.added|length == 2 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + +- name: re-set two test variables + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: abc123 + SECRET_ACCESS_KEY: 321cba + register: gitlab_project_variable_state + +- name: re-set two test variables state must not be changed + assert: + that: + - gitlab_project_variable_state is not changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 2 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + +- name: edit one variable + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: changed + purge: false + register: gitlab_project_variable_state + +- name: edit one variable state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 1 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 1 + - gitlab_project_variable_state.project_variable.updated[0] == "ACCESS_KEY_ID" + +- name: append one variable + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + some: value + purge: false + register: gitlab_project_variable_state + +- name: append one variable state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 1 + - gitlab_project_variable_state.project_variable.untouched|length == 2 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + - gitlab_project_variable_state.project_variable.added[0] == "some" + +- name: re-set all variables + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + ACCESS_KEY_ID: changed + SECRET_ACCESS_KEY: 321cba + some: value + register: gitlab_project_variable_state + +- name: re-set all variables state must not be changed + assert: + that: + - not gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 3 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + +- name: set one variables and purge all others + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + some: value + purge: true + register: gitlab_project_variable_state + +- name: set one variables and purge all others state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 1 + - gitlab_project_variable_state.project_variable.removed|length == 2 + - gitlab_project_variable_state.project_variable.updated|length == 0 + +- name: only one variable is left + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + some: value + purge: false + register: gitlab_project_variable_state + +- name: only one variable is left state must not be changed + assert: + that: + - not gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 1 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + - gitlab_project_variable_state.project_variable.untouched[0] == "some" + +- name: test integer values + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + some: 42 + purge: false + register: gitlab_project_variable_state + +- name: only one variable is left state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 1 + +- name: test float values + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + some: 42.23 + purge: false + register: gitlab_project_variable_state + +- name: only one variable is left state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 1 + +- name: delete the last left variable + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + state: absent + vars: + some: value + register: gitlab_project_variable_state + +- name: no variable is left state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 1 + - gitlab_project_variable_state.project_variable.updated|length == 0 + - gitlab_project_variable_state.project_variable.removed[0] == "some" + +- name: add one variable with variable_type file + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + variables: + - name: my_test_var + value: my_test_value + variable_type: file + purge: false + register: gitlab_project_variable_state + +- name: append one variable state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 1 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + # VALUE_SPECIFIED_IN_NO_LOG_PARAMETER + #- gitlab_project_variable_state.project_variable.added[0] == "my_test_var" + +- name: change variable_type attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + my_test_var: + value: my_test_value + variable_type: env_var + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: revert variable_type attribute + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + my_test_var: + value: my_test_value + variable_type: file + register: gitlab_project_variable_state + +- name: state must be changed + assert: + that: + - gitlab_project_variable_state is changed + +- name: delete the variable_type file variable + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + state: absent + vars: + my_test_var: my_test_value + register: gitlab_project_variable_state + +- name: no variable is left state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 1 + - gitlab_project_variable_state.project_variable.updated|length == 0 + - gitlab_project_variable_state.project_variable.removed[0] == "my_test_var" + +- name: set complete page and purge existing ones + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + page1_var01: value + page1_var02: value + page1_var03: value + page1_var04: value + page1_var05: value + page1_var06: value + page1_var07: value + page1_var08: value + page1_var09: value + page1_var10: value + page1_var11: value + page1_var12: value + page1_var13: value + page1_var14: value + page1_var15: value + page1_var16: value + page1_var17: value + page1_var18: value + page1_var19: value + page1_var20: value + purge: true + register: gitlab_project_variable_state + +- name: complete page added state must be changed + assert: + that: + - gitlab_project_variable_state is changed + - gitlab_project_variable_state.project_variable.added|length == 20 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + +- name: set complete page and keep existing ones + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + vars: + page2_var01: value + page2_var02: value + page2_var03: value + page2_var04: value + page2_var05: value + page2_var06: value + page2_var07: value + page2_var08: value + page2_var09: value + page2_var10: value + page2_var11: value + page2_var12: value + page2_var13: value + page2_var14: value + page2_var15: value + page2_var16: value + page2_var17: value + page2_var18: value + page2_var19: value + page2_var20: value + purge: false + register: gitlab_project_variable_state + +- name: existing page untouched state must be changed + assert: + that: + - gitlab_project_variable_state is changed + - gitlab_project_variable_state.project_variable.added|length == 20 + - gitlab_project_variable_state.project_variable.untouched|length == 20 + +- name: check that no variables are left + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + purge: true + register: gitlab_project_variable_state + +- name: check that no variables are untouched state must be changed + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 0 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 40 + - gitlab_project_variable_state.project_variable.updated|length == 0 + +- name: same vars, diff scope + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + purge: true + variables: + - name: SECRET_ACCESS_KEY + value: 3214cbad + masked: true + protected: true + variable_type: env_var + environment_scope: production + - name: SECRET_ACCESS_KEY + value: hello_world + masked: true + protected: true + variable_type: env_var + environment_scope: development + register: gitlab_project_variable_state + +- name: verify two vars + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.added|length == 2 + - gitlab_project_variable_state.project_variable.untouched|length == 0 + - gitlab_project_variable_state.project_variable.removed|length == 0 + - gitlab_project_variable_state.project_variable.updated|length == 0 + +- name: throw error when state is present but no value is given + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + variables: + - name: delete_me + register: gitlab_project_variable_state + ignore_errors: true + +- name: verify fail + assert: + that: + - gitlab_project_variable_state.failed + - gitlab_project_variable_state is not changed + +- name: set a new variable to delete it later + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + purge: true + variables: + - name: delete_me + value: ansible + register: gitlab_project_variable_state + +- name: verify the change + assert: + that: + - gitlab_project_variable_state.changed + +- name: delete variable without referencing its value + gitlab_project_variable: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + project: "{{ gitlab_project_name }}" + state: absent + variables: + - name: delete_me + register: gitlab_project_variable_state + +- name: verify deletion + assert: + that: + - gitlab_project_variable_state.changed + - gitlab_project_variable_state.project_variable.removed|length == 1 diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_runner/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_runner/aliases new file mode 100644 index 000000000..fc0e157c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_runner/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_runner/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_runner/defaults/main.yml new file mode 100644 index 000000000..ec7c0cfe1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_runner/defaults/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 + +gitlab_project_name: ansible_test_project +gitlab_hook_url: http://gitlab.example.com/hook +gitlab_runner_name: ansible_test_runner diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_runner/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_runner/tasks/main.yml new file mode 100644 index 000000000..467e918c2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_runner/tasks/main.yml @@ -0,0 +1,78 @@ +--- +#################################################################### +# 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: Create {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: present + +- name: Cleanup GitLab runner + gitlab_runner: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + description: "{{ gitlab_runner_name }}" + registration_token: "{{ gitlab_runner_registration_token }}" + state: absent + +- name: Create GitLab Runner + gitlab_runner: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + description: "{{ gitlab_runner_name }}" + registration_token: "{{ gitlab_runner_registration_token }}" + state: present + register: gitlab_runner_state + +- name: Test group created + assert: + that: + - gitlab_runner_state is changed + + +#### COMMENTED AS MODULE WILL UPDATE THE RUNNER IF EXISTS. TO BE DISCUSSED #### +# - name: Create GitLab Runner ( Idempotency test ) +# gitlab_runner: +# server_url: "{{ gitlab_host }}" +# validate_certs: false +# login_token: "{{ gitlab_login_token }}" +# description: "{{ gitlab_runner_name }}" +# registration_token: "{{ gitlab_runner_registration_token }}" +# state: present +# register: gitlab_runner_state_again + +# - name: Test module is idempotent +# assert: +# that: +# - gitlab_runner_state_again is not changed + +- name: Remove GitLab Runner + gitlab_runner: + server_url: "{{ gitlab_host }}" + validate_certs: false + login_token: "{{ gitlab_login_token }}" + description: "{{ gitlab_runner_name }}" + registration_token: "{{ gitlab_runner_registration_token }}" + state: absent + register: gitlab_runner_state_absent + +- name: Assert runner has been removed + assert: + that: + - gitlab_runner_state_absent is changed diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_user/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_user/aliases new file mode 100644 index 000000000..fc0e157c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_user/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_user/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_user/defaults/main.yml new file mode 100644 index 000000000..c7a4a5dd3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_user/defaults/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 + +gitlab_user: ansible_test_user +gitlab_user_pass: Secr3tPassw00rd +gitlab_user_email: root@localhost +gitlab_sshkey_name: ansibletest +gitlab_sshkey_file: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDI8GIMlrirf+zsvBpxnF0daykP6YEJ5wytZXhDGD2dZXg9Tln0KUSDgreT3FDgoabjlOmG1L/nhu6ML76WCsmc/wnVMlXlDlQpVJSQ2PCxGNs9WRW7Y/Pk6t9KtV/VSYr0LaPgLEU8VkffSUBJezbKa1cssjb4CmRRqcePRNYpgCXdK05TEgFvmXl9qIM8Domf1ak1PlbyMmi/MytzHmnVFzxgUKv5c0Mr+vguCi131gPdh3QSf5AHPLEoO9LcMfu2IO1zvl61wYfsJ0Wn2Fncw+tJQfUin0ffTFgUIsGqki04/YjXyWynjSwQf5Jym4BYM0i2zlDUyRxs4/Tfp4yvJFik42ambzjLK6poq+iCpQReeYih9WZUaZwUQe7zYWhTOuoV7ydsk8+kDRMPidF9K5zWkQnglGrOzdbTqnhxNpwHCg2eSRJ49kPYLOH76g8P7IQvl+zluG0o8Nndir1WcYil4D4CCBskM8WbmrElZH1CRyP/NQMNIf4hFMItTjk= ansible@ansible +gitlab_sshkey_expires_at: 2030-01-01T00:00:00.000Z diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/main.yml new file mode 100644 index 000000000..e8c1ec360 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/main.yml @@ -0,0 +1,257 @@ +--- +#################################################################### +# 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: Clean up gitlab user + gitlab_user: + api_url: "{{ gitlab_host }}" + name: ansible_test_user + username: ansible_test_user + password: Secr3tPassw00rd + email: root@localhost + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: absent + + +- name: Create gitlab user + gitlab_user: + api_url: "{{ gitlab_host }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check user has been created correctly + assert: + that: + - gitlab_user_state is changed + +- name: Create gitlab user again + gitlab_user: + api_url: "{{ gitlab_host }}" + email: root@localhost + name: ansible_test_user + username: ansible_test_user + password: Secr3tPassw00rd + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state_again + +- name: Check state is not changed + assert: + that: + - gitlab_user_state_again is not changed + - gitlab_user_state_again.user.is_admin == False + + +- name: Update User Test => Make User Admin + gitlab_user: + api_url: "{{ gitlab_host }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + isadmin: true + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check if user is admin now + assert: + that: + - gitlab_user_state is changed + - gitlab_user_state.user.is_admin == True + +- name: Update User Test => Make User Admin (Again) + gitlab_user: + api_url: "{{ gitlab_host }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + isadmin: true + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check state is not changed + assert: + that: + - gitlab_user_state is not changed + - gitlab_user_state.user.is_admin == True + +- name: Update User Test => Remove Admin Rights + gitlab_user: + api_url: "{{ gitlab_host }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + isadmin: false + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check if user is not admin anymore + assert: + that: + - gitlab_user_state is changed + - gitlab_user_state.user.is_admin == False + + +- name: Update User Test => Try Changing Mail without Confirmation Skipping + gitlab_user: + api_url: "{{ gitlab_host }}" + email: foo@bar.baz + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + confirm: true + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check that eMail is unchanged (Only works with confirmation skipping) + assert: + that: + - gitlab_user_state is changed + - gitlab_user_state.user.email == gitlab_user_email + +- name: Update User Test => Change Mail with Confirmation Skip + gitlab_user: + api_url: "{{ gitlab_host }}" + email: foo@bar.baz + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + confirm: false + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check that mail has changed now + assert: + that: + - gitlab_user_state is changed + - gitlab_user_state.user.email == 'foo@bar.baz' + +- name: Update User Test => Change Mail with Confirmation Skip (Again) + gitlab_user: + api_url: "{{ gitlab_host }}" + email: foo@bar.baz + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + confirm: false + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check state is not changed + assert: + that: + - gitlab_user_state is not changed + - gitlab_user_state.user.email == 'foo@bar.baz' + +- name: Update User Test => Revert to original Mail Address + gitlab_user: + api_url: "{{ gitlab_host }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + confirm: false + validate_certs: false + api_token: "{{ gitlab_login_token }}" + state: present + register: gitlab_user_state + +- name: Check that reverting mail back to original has worked + assert: + that: + - gitlab_user_state is changed + - gitlab_user_state.user.email == gitlab_user_email + + +- name: Update User Test => Change User Password + gitlab_user: + api_url: "{{ gitlab_host }}" + validate_certs: false + + # note: the only way to check if a password really is what it is expected + # to be is to use it for login, so we use it here instead of the + # default token assuming that a user can always change its own password + api_username: "{{ gitlab_user }}" + api_password: "{{ gitlab_user_pass }}" + + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: new-super-password + state: present + register: gitlab_user_state + +- name: Check PW setting return state + assert: + that: + # note: there is no way to determine if a password has changed or + # not, so it can only be always yellow or always green, we + # decided for always green for now + - gitlab_user_state is not changed + +- name: Update User Test => Reset User Password + gitlab_user: + api_url: "{{ gitlab_host }}" + validate_certs: false + + api_username: "{{ gitlab_user }}" + api_password: new-super-password + + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + state: present + register: gitlab_user_state + +- name: Check PW setting return state (Again) + assert: + that: + - gitlab_user_state is not changed + +- name: Update User Test => Check that password was reset + gitlab_user: + api_url: "{{ gitlab_host }}" + validate_certs: false + + api_username: "{{ gitlab_user }}" + api_password: "{{ gitlab_user_pass }}" + + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + state: present + register: gitlab_user_state + +- name: Check PW setting return state (Reset) + assert: + that: + - gitlab_user_state is not changed + +- include_tasks: sshkey.yml diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/sshkey.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/sshkey.yml new file mode 100644 index 000000000..bba724d5e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/gitlab_user/tasks/sshkey.yml @@ -0,0 +1,139 @@ +--- +#################################################################### +# 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: Create gitlab user with sshkey credentials + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + state: present + register: gitlab_user_sshkey + +- name: Check user has been created correctly + assert: + that: + - gitlab_user_sshkey is changed + +- name: Create gitlab user again + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + state: present + register: gitlab_user_sshkey_again + +- name: Check state is not changed + assert: + that: + - gitlab_user_sshkey_again is not changed + +- name: Add expires_at to an already created gitlab user with ssh key + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + sshkey_expires_at: "{{ gitlab_sshkey_expires_at }}" + state: present + register: gitlab_user_created_user_sshkey_expires_at + +- name: Check expires_at will not be added to a present ssh key + assert: + that: + - gitlab_user_created_user_sshkey_expires_at is not changed + +- name: Remove created gitlab user + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + validate_certs: false + state: absent + register: gitlab_user_sshkey_remove + +- name: Check user has been removed correctly + assert: + that: + - gitlab_user_sshkey_remove is changed + +- name: Create gitlab user with sshkey and expires_at + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + sshkey_expires_at: "{{ gitlab_sshkey_expires_at }}" + state: present + register: gitlab_user_sshkey_expires_at + +- name: Check user has been created correctly + assert: + that: + - gitlab_user_sshkey_expires_at is changed + +- name: Create gitlab user with sshkey and expires_at again + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + password: "{{ gitlab_user_pass }}" + validate_certs: false + sshkey_name: "{{ gitlab_sshkey_name }}" + sshkey_file: "{{ gitlab_sshkey_file }}" + sshkey_expires_at: "{{ gitlab_sshkey_expires_at }}" + state: present + register: gitlab_user_sshkey_expires_at_again + +- name: Check state is not changed + assert: + that: + - gitlab_user_sshkey_expires_at_again is not changed + +- name: Remove created gitlab user + gitlab_user: + api_url: "{{ gitlab_host }}" + api_token: "{{ gitlab_login_token }}" + email: "{{ gitlab_user_email }}" + name: "{{ gitlab_user }}" + username: "{{ gitlab_user }}" + validate_certs: false + state: absent + register: gitlab_user_sshkey_expires_at_remove + +- name: Check user has been removed correctly + assert: + that: + - gitlab_user_sshkey_expires_at_remove is changed diff --git a/ansible_collections/community/general/tests/integration/targets/hg/aliases b/ansible_collections/community/general/tests/integration/targets/hg/aliases new file mode 100644 index 000000000..e1d7ab2a2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hg/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 +skip/python3 +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/hg/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/hg/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hg/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/hg/tasks/install.yml b/ansible_collections/community/general/tests/integration/targets/hg/tasks/install.yml new file mode 100644 index 000000000..1b8916880 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hg/tasks/install.yml @@ -0,0 +1,88 @@ +--- +# 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: get the default python version + command: "{{ ansible_python_interpreter }} -V" + register: default_python_version + +- name: find the default python + command: which python + register: which_python + +- name: find the default pip + command: which pip + register: which_pip + +- name: preserve the default python + command: cp -av "{{ which_python.stdout }}" "{{ which_python.stdout }}.default" + +- name: preserve the default pip + command: cp -av "{{ which_pip.stdout }}" "{{ which_pip.stdout }}.default" + +# using the apt module prevents autoremove from working, so call apt-get via shell instead +- name: install mercurial (apt) + shell: apt-get -y update && apt-get -y install mercurial + when: ansible_facts.pkg_mgr == 'apt' + +- name: install mercurial (dnf) + dnf: + name: mercurial + when: ansible_facts.pkg_mgr == 'dnf' + +- name: install mercurial (yum) + yum: + name: mercurial + when: ansible_facts.pkg_mgr == 'yum' + +- name: install mercurial (pkgng) + package: + name: mercurial + when: ansible_facts.pkg_mgr in ['pkgng', 'community.general.pkgng'] + +- name: install mercurial (zypper) + package: + name: mercurial + when: ansible_facts.pkg_mgr in ['zypper', 'community.general.zypper'] + +- name: preserve the updated python + command: cp -av "{{ which_python.stdout }}" "{{ which_python.stdout }}.updated" + +- name: preserve the updated pip + command: cp -av "{{ which_pip.stdout }}" "{{ which_pip.stdout }}.updated" + +- name: locate mercurial + command: which hg + register: which_hg + +- name: get the mercurial interpreter + command: head -n 1 "{{ which_hg.stdout }}" + register: hg_interpreter + +- name: stat the mercurial interpreter + stat: + path: "{{ hg_interpreter.stdout[2:] }}" + register: stat_hg_interpreter + +- name: bypass the mercurial python interpreter symlink (if needed) + lineinfile: + path: "{{ which_hg.stdout }}" + regexp: "^#!.*$" + line: "#!{{ stat_hg_interpreter.stat.lnk_source }}" + when: stat_hg_interpreter.stat.islnk + +- name: restore the default python + command: cp -av "{{ which_python.stdout }}.default" "{{ which_python.stdout }}" + +- name: restore the default pip + command: cp -av "{{ which_pip.stdout }}.default" "{{ which_pip.stdout }}" + +- name: get the current python version + command: "{{ ansible_python_interpreter }} -V" + register: current_python_version + +- name: verify the python version has not changed + assert: + that: + - default_python_version.stdout == current_python_version.stdout diff --git a/ansible_collections/community/general/tests/integration/targets/hg/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hg/tasks/main.yml new file mode 100644 index 000000000..1ca30459c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hg/tasks/main.yml @@ -0,0 +1,45 @@ +#################################################################### +# 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 hg module +# Copyright (c) 2014, James Tanner +# +# 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: determine if mercurial is already installed + command: which hg + register: has_hg + ignore_errors: true + +- name: warn if the underlying system is not capable of running these tests + debug: + msg: >- + The mercurial client is not able to check out Bitbucket repositories as per the changes mentioned here: + https://bitbucket.org/blog/deprecating-tlsv1-tlsv1-1-2018-12-01 . Therefore these tests are skipped. + when: (ansible_distribution == "Ubuntu" and ansible_distribution_version == "14.04") or ansible_python_version is version("2.7.9", "<") + +- block: + - name: install mercurial + include_tasks: install.yml + when: has_hg is failed + + - name: test mercurial + include_tasks: run-tests.yml + + - name: uninstall mercurial + include_tasks: uninstall.yml + when: has_hg is failed + + # As per the bitbucket changes in https://bitbucket.org/blog/deprecating-tlsv1-tlsv1-1-2018-12-01 , this + # test will fail under certain circumstances, to avoid false positives, we skip these tests under the following + # circumstances: + # + # - The ubuntu 14.04 image used on shippable runs python 2.7.6, so we skip explicitly for this image. + # - When ansible_python_version is not 2.7.9 or higher, mercurial is likely to also run using this same (old) + # python version, which causes issues as per the link above. + when: + - not (ansible_distribution == "Ubuntu" and ansible_distribution_version == "14.04") + - ansible_python_version is version("2.7.9", ">=") diff --git a/ansible_collections/community/general/tests/integration/targets/hg/tasks/run-tests.yml b/ansible_collections/community/general/tests/integration/targets/hg/tasks/run-tests.yml new file mode 100644 index 000000000..928b7cb68 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hg/tasks/run-tests.yml @@ -0,0 +1,85 @@ +# test code for the hg module +# Copyright (c) 2018, 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: set where to extract the repo + set_fact: + checkout_dir: "{{ remote_tmp_dir }}/hg_project_test" + +- name: set what repo to use + set_fact: + repo: "http://hg.pf.osdn.net/view/a/ak/akasurde/hg_project_test" + +- name: clean out the remote_tmp_dir + shell: rm -rf {{ remote_tmp_dir }}/* + +- name: verify that mercurial is installed so this test can continue + shell: which hg + +- name: initial checkout + hg: + repo: "{{ repo }}" + dest: "{{ checkout_dir }}" + register: hg_result + +- debug: var=hg_result + +- shell: ls {{ checkout_dir }} + +- name: verify information about the initial clone + assert: + that: + - "'before' in hg_result" + - "'after' in hg_result" + - "not hg_result.before" + - "hg_result.changed" + +- name: repeated checkout + hg: + repo: "{{ repo }}" + dest: "{{ checkout_dir }}" + register: hg_result2 + +- debug: var=hg_result2 + +- name: check for tags + stat: + path: "{{ checkout_dir }}/.hgtags" + register: tags + +- name: check for remotes + stat: + path: "{{ checkout_dir }}/.hg/branch" + register: branches + +- debug: var=tags +- debug: var=branches + +- name: assert presence of tags/trunk/branches + assert: + that: + - "tags.stat.isreg" + - "branches.stat.isreg" + +- name: verify on a re-clone things are marked unchanged + assert: + that: + - "not hg_result2.changed" + +- name: Checkout non-existent repo clone + hg: + repo: "http://hg.pf.osdn.net/view/a/ak/akasurde/hg_project_test_1" + clone: false + update: false + register: hg_result3 + ignore_errors: true + +- name: Verify result of non-existent repo clone + assert: + that: + - hg_result3.msg + - "'abort: HTTP Error 404: Not Found' in hg_result3.msg" + - "not hg_result3.changed" diff --git a/ansible_collections/community/general/tests/integration/targets/hg/tasks/uninstall.yml b/ansible_collections/community/general/tests/integration/targets/hg/tasks/uninstall.yml new file mode 100644 index 000000000..4a26995ef --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hg/tasks/uninstall.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 + +- name: restore the updated python + command: mv "{{ which_python.stdout }}.updated" "{{ which_python.stdout }}" + +- name: restore the updated pip + command: mv "{{ which_pip.stdout }}.updated" "{{ which_pip.stdout }}" + +- name: restore the mercurial python interpreter symlink (if needed) + lineinfile: + path: "{{ which_hg.stdout }}" + regexp: "^#!.*$" + line: "#!{{ stat_hg_interpreter.stat.path }}" + when: stat_hg_interpreter.stat.islnk + +# using the apt module prevents autoremove from working, so call apt-get via shell instead +- name: uninstall packages which were not originally installed (apt) + shell: apt-get -y remove mercurial && apt-get -y autoremove + when: ansible_facts.pkg_mgr == 'apt' + +- name: uninstall packages which were not originally installed (dnf) + dnf: + name: mercurial + state: absent + autoremove: true + when: ansible_facts.pkg_mgr == 'dnf' + +# the yum module does not have an autoremove parameter +- name: uninstall packages which were not originally installed (yum) + shell: yum -y autoremove mercurial + when: ansible_facts.pkg_mgr == 'yum' + +- name: uninstall packages which were not originally installed (pkgng) + package: + name: mercurial + state: absent + autoremove: true + when: ansible_facts.pkg_mgr in ['pkgng', 'community.general.pkgng'] + +- name: uninstall packages which were not originally installed (zypper) + package: + name: mercurial + state: absent + when: ansible_facts.pkg_mgr in ['zypper', 'community.general.zypper'] + +- name: restore the default python + raw: mv "{{ which_python.stdout }}.default" "{{ which_python.stdout }}" + +- name: restore the default pip + raw: mv "{{ which_pip.stdout }}.default" "{{ which_pip.stdout }}" diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/aliases b/ansible_collections/community/general/tests/integration/targets/homebrew/aliases new file mode 100644 index 000000000..11bb9a086 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/freebsd +skip/rhel +skip/docker +skip/python2.6 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 new file mode 100644 index 000000000..1db3ef1a6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.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 +# 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_cask/aliases b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases new file mode 100644 index 000000000..11bb9a086 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/freebsd +skip/rhel +skip/docker +skip/python2.6 diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew_cask/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/defaults/main.yml new file mode 100644 index 000000000..18ebd7b10 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/defaults/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 + +cask: brooklyn diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew_cask/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/tasks/main.yml new file mode 100644 index 000000000..85f257266 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/tasks/main.yml @@ -0,0 +1,73 @@ +--- +#################################################################### +# 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_cask module. +# Copyright (c) 2022, Joseph Torcasso +# 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'] + + - block: + - name: Install cask + homebrew_cask: + name: "{{ cask }}" + state: present + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + register: cask_install_result + + - assert: + that: + - cask_install_result is changed + - "'Cask installed' in cask_install_result.msg" + + - name: Install cask (idempotence) + homebrew_cask: + name: "{{ cask }}" + state: present + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + register: cask_install_result + + - assert: + that: + - cask_install_result is not changed + - "'Cask installed' not in cask_install_result.msg" + - "'Cask already installed' in cask_install_result.msg" + + - name: Install cask with force install option + homebrew_cask: + name: "{{ cask }}" + state: present + install_options: force + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + register: cask_install_result + + - assert: + that: + - cask_install_result is changed + - "'Cask installed' in cask_install_result.msg" + + always: + - name: Delete cask + homebrew_cask: + name: "{{ cask }}" + state: absent + install_options: force + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + ignore_errors: true diff --git a/ansible_collections/community/general/tests/integration/targets/homectl/aliases b/ansible_collections/community/general/tests/integration/targets/homectl/aliases new file mode 100644 index 000000000..b87db2e43 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homectl/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/1 +skip/aix +skip/freebsd +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/ diff --git a/ansible_collections/community/general/tests/integration/targets/homectl/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/homectl/tasks/main.yml new file mode 100644 index 000000000..93c1089b4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homectl/tasks/main.yml @@ -0,0 +1,182 @@ +--- +# 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 + +# Get systemd version and if it doesn't exist don't run these tests. +- name: check systemd version + command: "systemctl --version" + register: systemd_version + ignore_errors: true + +- name: check homectl version + command: homectl --version + register: homectl_version + ignore_errors: true + +- block: + - name: Check and start systemd-homed service + service: + name: systemd-homed.service + state: started + enabled: true + + - name: Add a user 'james' + community.general.homectl: + name: james + password: myreallysecurepassword1! + state: present + + - name: verify user added + command: homectl inspect james + register: james_info + + - name: Add the user 'tom' with a zsh shell, uid of 1000, and gid of 1000 + community.general.homectl: + name: tom + password: myreallysecurepassword1! + state: present + shell: /bin/zsh + uid: 1000 + gid: 1000 + disksize: 10G + register: tom_userinfo + + - name: Try to add user 'james' that already exists + community.general.homectl: + name: james + password: myreallysecurepassword1! + state: present + shell: /bin/ksh + register: user_exists + + - name: Try to use 'resize=yes' option without 'disksize' option (not allowed) + community.general.homectl: + name: foo + password: uq4895738!@#$%dfd + state: present + resize: true + register: resize_out + ignore_errors: true + + - name: Use option 'disksize=1G' without option resize (allowed) + community.general.homectl: + name: foobar + password: "uq4895738!@#$%dfd" + state: present + disksize: 1G + register: disk_out + ignore_errors: true + + - name: Try to Create user without giving password + community.general.homectl: + name: danielle + register: danielle_out + ignore_errors: true + + - name: remove user 'foobar' without requiring password + community.general.homectl: + name: foobar + state: absent + register: delete_foobar_out + + - name: modify user 'james' to have zsh shell and timezone 'America/New_York' + community.general.homectl: + name: james + password: myreallysecurepassword1! + state: present + shell: /bin/zsh + timezone: America/New_York + register: lukuser_modify_out + + - name: create user 'jake' with all mount options + community.general.homectl: + name: jake + password: myreallysecurepassword12! + mountopts: noexec,nosuid,nodev + sshkeys: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDUSW/q2qFZPr2vS0qrmAs+1iQI1jLIBdJ4CVIhE3KnIwxkkiInS8mJ+t0FBTeK3ks3GZLPVYC1v9o2P+oqyUk1CiBnCsMXGJud+L/t8b5r8MiJMyP7Jzd6yhmcvenjvz+vY06jQ9chWAtThEknuaOMongIpQQzSLGbdMy0yMsz4GEjicwdcj1PDwItPvUt4TL4K7V9NE672idADlRt6qng4UwpziqlYgsyIG46ettDras8hGAPricrhFWUS2rLDsCD0thkPFdR8HL1ZWTZ6LtolhO4MYtgntzXn708TTmFC2oIDluzyxVoUYmsfVotVdXFZcOWffnwbCgU+tn75JXTLozgTbV3VWmkxpJFErCWPerxcZv3+7b0f36/Y0gRNjM9HERLDSE1c8yz29NOLY0qH5306aByjOaerxNq9+ZOU/Fmf5/VfGIUp/FdLxDw+V0AzejFG580VAcstEMsOHSdwTbi3gf6LoGSiRyWKKDod0TZCMC6RzfdsfdsfI9CClGl0s= test@router.home" + register: jake_out + + - name: Try to remove user 'janet' that doesn't exist + community.general.homectl: + name: janet + state: absent + register: user_not_exist + ignore_errors: true + + - name: Use check_mode to try and create user 'diana' + community.general.homectl: + name: diana + password: helloworld123!@ + state: present + check_mode: true + register: diana_create_checkmode_out + + - name: Verify user 'diana' was not created with check_mode + command: homectl inspect diana + register: user_diana_exists + ignore_errors: true + + - name: Try to modify user 'jake' with only noexec mount option in check_mode + community.general.homectl: + name: jake + password: myreallysecurepassword12! + state: present + mountopts: noexec + check_mode: true + register: jake_checkmode_out + + - name: Verify user 'jake' was not modified and still has all mount options + command: homectl inspect jake + register: user_jake_details_out + + - name: Modify user 'jake' with only noexec mount option + community.general.homectl: + name: jake + password: myreallysecurepassword12! + state: present + mountopts: noexec + register: jake_modify_out + + - name: modify user 'jake' again with only noexec mount option to make sure changed is false as nothing has changed. + community.general.homectl: + name: jake + password: myreallysecurepassword12! + state: present + mountopts: noexec + register: jake_modify_again_out + + - name: Try to modify user 'jake' with an incorrect password + community.general.homectl: + name: jake + password: incorrectPassword! + state: present + mountopts: noexec + locked: true + ignore_errors: true + register: jake_incorrect_pass_out + + - assert: + that: + - james_info.rc == 0 + - tom_userinfo.data['gid'] == 1000 and tom_userinfo.data['uid'] == 1000 + - user_exists is changed and user_exists.data['shell'] == '/bin/ksh' + - resize_out is not changed + - disk_out is changed + - delete_foobar_out is changed + - danielle_out is not changed + - lukuser_modify_out.data['timeZone'] == "America/New_York" and lukuser_modify_out.data['shell'] == "/bin/zsh" + - user_not_exist is not changed and user_not_exist.msg == "User does not exist!" + - jake_out is changed and jake_out.data['mountNoDevices'] == True and jake_out.data['mountNoSuid'] == True and jake_out.data['mountNoExecute'] == True + - diana_create_checkmode_out is changed and 'No home for user diana known' in user_diana_exists.stderr + - "jake_checkmode_out is changed and 'Mount Flags: nosuid nodev noexec' in user_jake_details_out.stdout" + - jake_modify_out is changed and jake_modify_out.data['privileged']['sshAuthorizedKeys'] is not none + - jake_modify_out.data['mountNoDevices'] == False and jake_modify_out.data['mountNoExecute'] == True and jake_modify_out.data['mountNoSuid'] == False + - jake_modify_again_out is not changed + - jake_incorrect_pass_out is not changed and jake_incorrect_pass_out is failed and jake_incorrect_pass_out.msg == 'User exists but password is incorrect!' + + # homectl was first introduced in systemd 245 so check version >= 245 and make sure system has systemd and homectl command + when: + - systemd_version.rc == 0 and (systemd_version.stdout | regex_search('[0-9][0-9][0-9]') | int >= 245) and homectl_version.rc == 0 + - ansible_distribution != 'Archlinux' # TODO! + - ansible_distribution != 'Fedora' or ansible_distribution_major_version|int < 36 # TODO! diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/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/hwc_ecs_instance/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/tasks/main.yml new file mode 100644 index 000000000..dd7086152 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_ecs_instance/tasks/main.yml @@ -0,0 +1,319 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: present + register: vpc +- name: create a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: present + register: subnet +- name: create a eip + hwc_vpc_eip: + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + type: "5_bgp" + state: present + register: eip +- name: create a disk + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: present + register: disk +- name: delete a instance + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: absent +#---------------------------------------------------------- +- name: create a instance (check mode) + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a instance + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: create a instance (idemponent) + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: present + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a instance that already exists + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a instance (check mode) + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a instance + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: absent + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: delete a instance (idemponent) + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: absent + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a instance that does not exist + hwc_ecs_instance: + data_volumes: + - volume_id: "{{ disk.id }}" + enable_auto_recovery: false + eip_id: "{{ eip.id }}" + name: "ansible_ecs_instance_test" + availability_zone: "cn-north-1a" + nics: + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + - subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.34" + server_tags: + my_server: "my_server" + image_id: "8da46d6d-6079-4e31-ad6d-a7167efff892" + flavor_name: "s3.small.1" + vpc_id: "{{ vpc.id }}" + root_volume: + volume_type: "SAS" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a disk + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: absent + register: disk +- name: delete a eip + hwc_vpc_eip: + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + type: "5_bgp" + state: absent + register: eip +- name: delete a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: absent + register: subnet +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: absent + register: vpc diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/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/hwc_evs_disk/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/tasks/main.yml new file mode 100644 index 000000000..63b7d03f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_evs_disk/tasks/main.yml @@ -0,0 +1,113 @@ +--- +#################################################################### +# 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: delete a disk + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: absent +#---------------------------------------------------------- +- name: create a disk + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +# ------------------------------------------------------------ +- name: test create a disk in check mode + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: present + register: result + check_mode: true +- name: verify results of test create a disk in check mode + assert: + that: + result is changed +# ---------------------------------------------------------------------------- +- name: create a disk that already exists + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a disk (check mode) + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: delete a disk + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: absent + register: result +- name: assert changed is true + assert: + that: + result is changed +# ---------------------------------------------------------------------------- +- name: delete a disk that does not exist (check mode) + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: absent + check_mode: true + register: result +- name: assert changed is false + assert: + that: + - result is not changed +# ---------------------------------------------------------------------------- +- name: delete a disk that does not exist + hwc_evs_disk: + availability_zone: "cn-north-1a" + name: "ansible_evs_disk_test" + volume_type: "SATA" + size: 10 + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/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/hwc_network_vpc/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/tasks/main.yml new file mode 100644 index 000000000..3695fd210 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_network_vpc/tasks/main.yml @@ -0,0 +1,105 @@ +--- +#################################################################### +# 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 + +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file at +# https://www.github.com/huaweicloud/magic-modules +# +# ---------------------------------------------------------------------------- +# Pre-test setup +- name: delete a vpc + hwc_network_vpc: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "vpc_1" + cidr: "192.168.100.0/24" + state: absent +#---------------------------------------------------------- +- name: create a vpc + hwc_network_vpc: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "vpc_1" + cidr: "192.168.100.0/24" + state: present + register: result +- name: assert changed is true + assert: + that: + - result is changed +# ---------------------------------------------------------------------------- +- name: create a vpc that already exists + hwc_network_vpc: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "vpc_1" + cidr: "192.168.100.0/24" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a vpc + hwc_network_vpc: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "vpc_1" + cidr: "192.168.100.0/24" + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +# ---------------------------------------------------------------------------- +- name: delete a vpc that does not exist + hwc_network_vpc: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "vpc_1" + cidr: "192.168.100.0/24" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/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/hwc_smn_topic/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/tasks/main.yml new file mode 100644 index 000000000..323904773 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_smn_topic/tasks/main.yml @@ -0,0 +1,86 @@ +--- +#################################################################### +# 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: delete a smn topic + hwc_smn_topic: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "ansible_smn_topic_test" + state: absent +#---------------------------------------------------------- +- name: create a smn topic + hwc_smn_topic: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "ansible_smn_topic_test" + state: present + register: result +- name: assert changed is true + assert: + that: + - result is changed +# ---------------------------------------------------------------------------- +- name: create a smn topic that already exists + hwc_smn_topic: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "ansible_smn_topic_test" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a smn topic + hwc_smn_topic: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "ansible_smn_topic_test" + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +# ---------------------------------------------------------------------------- +- name: delete a smn topic that does not exist + hwc_smn_topic: + identity_endpoint: "{{ identity_endpoint }}" + user: "{{ user }}" + password: "{{ password }}" + domain: "{{ domain }}" + project: "{{ project }}" + region: "{{ region }}" + name: "ansible_smn_topic_test" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/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/hwc_vpc_eip/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/tasks/main.yml new file mode 100644 index 000000000..f830f951f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_eip/tasks/main.yml @@ -0,0 +1,190 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: present + register: vpc +- name: create a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: present + register: subnet +- name: create a port + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + register: port +- name: delete a eip + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: absent +#---------------------------------------------------------- +- name: create a eip (check mode) + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a eip + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: create a eip (idemponent) + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: present + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a eip that already exists + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a eip (check mode) + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a eip + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a eip (idemponent) + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: absent + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a eip that does not exist + hwc_vpc_eip: + type: "5_bgp" + dedicated_bandwidth: + charge_mode: "traffic" + name: "ansible_test_dedicated_bandwidth" + size: 1 + port_id: "{{ port.id }}" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a port + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + register: port +- name: delete a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: absent + register: subnet +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: absent + register: vpc diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/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/hwc_vpc_peering_connect/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml new file mode 100644 index 000000000..b8e02a539 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_peering_connect/tasks/main.yml @@ -0,0 +1,155 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_local" + state: present + register: vpc1 +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_peering" + state: present + register: vpc2 +- name: delete a peering connect + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: absent +#---------------------------------------------------------- +- name: create a peering connect (check mode) + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - not result.id + - result.changed +#---------------------------------------------------------- +- name: create a peering connect + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: create a peering connect (idemponent) + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: present + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a peering connect that already exists + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a peering connect (check mode) + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: delete a peering connect + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: absent + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: delete a peering connect (idemponent) + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: absent + check_mode: true + register: result +- name: assert changed is false + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a peering connect that does not exist + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_peering" + state: absent + register: vpc2 +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_local" + state: absent + register: vpc1 diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/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/hwc_vpc_port/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/tasks/main.yml new file mode 100644 index 000000000..93b17398f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_port/tasks/main.yml @@ -0,0 +1,141 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: present + register: vpc +- name: create a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: present + register: subnet +- name: delete a port + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent +#---------------------------------------------------------- +- name: create a port (check mode) + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a port + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a port (idemponent) + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a port that already exists + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a port (check mode) + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a port + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a port (idemponent) + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a port that does not exist + hwc_vpc_port: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: absent + register: subnet +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: absent + register: vpc diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/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/hwc_vpc_private_ip/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml new file mode 100644 index 000000000..6accdb855 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_private_ip/tasks/main.yml @@ -0,0 +1,142 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: present + register: vpc +- name: create a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: present + register: subnet +- name: delete a private ip + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent +#---------------------------------------------------------- +- name: create a private ip (check mode) + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a private ip + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a private ip (idemponent) + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a private ip that already exists + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a private ip (check mode) + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a private ip + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a private ip (idemponent) + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a private ip that does not exist + hwc_vpc_private_ip: + subnet_id: "{{ subnet.id }}" + ip_address: "192.168.100.33" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a subnet + hwc_vpc_subnet: + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + state: absent + register: subnet +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: absent + register: vpc diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/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/hwc_vpc_route/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/tasks/main.yml new file mode 100644 index 000000000..8e2e2ca82 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_route/tasks/main.yml @@ -0,0 +1,159 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_local" + state: present + register: vpc1 +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_peering" + state: present + register: vpc2 +- name: create a peering connect + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + filters: + - "name" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: present + register: connect +- name: delete a route + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: absent +#---------------------------------------------------------- +- name: create a route (check mode) + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + check_mode: true + register: result +- assert: + that: + - not result.id + - result.changed +#---------------------------------------------------------- +- name: create a route + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +# ---------------------------------------------------------- +- name: create a route (idemponent) + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: present + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ----------------------------------------------------------- +- name: create a route that already exists + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a route (check mode) + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: absent + check_mode: true +#---------------------------------------------------------- +- name: delete a route + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: absent + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: delete a (idemponent) + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: absent + check_mode: true + register: result +- name: not changed + assert: + that: + not result.changed +# ---------------------------------------------------------------------------- +- name: delete a route that does not exist + hwc_vpc_route: + vpc_id: "{{ vpc1.id }}" + destination: "192.168.0.0/16" + next_hop: "{{ connect.id }}" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a peering connect + hwc_vpc_peering_connect: + local_vpc_id: "{{ vpc1.id }}" + name: "ansible_network_peering_test" + filters: + - "name" + peering_vpc: + vpc_id: "{{ vpc2.id }}" + state: absent + register: connect +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_peering" + state: absent + register: vpc2 +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.0.0/16" + name: "ansible_network_vpc_test_local" + state: absent + register: vpc1 diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/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/hwc_vpc_security_group/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml new file mode 100644 index 000000000..b6ee25e25 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group/tasks/main.yml @@ -0,0 +1,91 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: delete a security group + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: absent +#---------------------------------------------------------- +- name: create a security group (check mode) + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - not result.id + - result.changed +#---------------------------------------------------------- +- name: create a security group + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: create a security group (idemponent) + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: present + check_mode: true + register: idemponent +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a security group that already exists + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a security group (check mode) + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: delete a security group + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: absent + register: result +- name: assert changed is true + assert: + that: + result is changed +# ---------------------------------------------------------------------------- +- name: delete a security group that does not exist + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/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/hwc_vpc_security_group_rule/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml new file mode 100644 index 000000000..4ce4bafdc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_security_group_rule/tasks/main.yml @@ -0,0 +1,166 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a security group + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: present + register: sg +- name: delete a security group rule + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: absent +#---------------------------------------------------------- +- name: create a security group rule (check mode) + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - not result.id + - result.changed +#---------------------------------------------------------- +- name: create a security group rule + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: present + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: create a security group rule (idemponent) + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: present + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a security group rule that already exists + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a security group rule (check mode) + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a security group rule + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a security group rule (idemponent) + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: absent + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a security group rule that does not exist + hwc_vpc_security_group_rule: + direction: "ingress" + protocol: "tcp" + ethertype: "IPv4" + port_range_max: 55 + security_group_id: "{{ sg.id }}" + port_range_min: 22 + remote_ip_prefix: "0.0.0.0/0" + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a security group + hwc_vpc_security_group: + name: "ansible_network_security_group_test" + state: absent + register: sg diff --git a/ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/aliases b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/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/hwc_vpc_subnet/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml new file mode 100644 index 000000000..522ffb601 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/hwc_vpc_subnet/tasks/main.yml @@ -0,0 +1,152 @@ +--- +#################################################################### +# 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 + +# Pre-test setup +- name: create a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: present + register: vpc +- name: delete a subnet + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: absent +#---------------------------------------------------------- +- name: create a subnet (check mode) + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: present + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - not result.id + - result.changed +#---------------------------------------------------------- +- name: create a subnet + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: present + register: result +- name: assert changed is true + assert: + that: + result is changed +#---------------------------------------------------------- +- name: create a subnet (idemponent) + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: present + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: create a subnet that already exists + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: present + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#---------------------------------------------------------- +- name: delete a subnet (check mode) + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: absent + check_mode: true + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a subnet + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: absent + register: result +- name: assert changed is true + assert: + that: + - result is changed +#---------------------------------------------------------- +- name: delete a subnet (idemponent) + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: absent + check_mode: true + register: result +- name: idemponent + assert: + that: + - not result.changed +# ---------------------------------------------------------------------------- +- name: delete a subnet that does not exist + hwc_vpc_subnet: + vpc_id: "{{ vpc.id }}" + cidr: "192.168.100.0/26" + gateway_ip: "192.168.100.32" + name: "ansible_network_subnet_test" + dhcp_enable: true + state: absent + register: result +- name: assert changed is false + assert: + that: + - result is not failed + - result is not changed +#--------------------------------------------------------- +# Post-test teardown +- name: delete a vpc + hwc_network_vpc: + cidr: "192.168.100.0/24" + name: "ansible_network_vpc_test" + state: absent + register: vpc diff --git a/ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/aliases b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/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/ilo_redfish_command/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/tasks/main.yml new file mode 100644 index 000000000..cc1fce748 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_command/tasks/main.yml @@ -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 + +- name: Wait for iLO Reboot Completion + community.general.ilo_redfish_command: + category: Systems + command: WaitforiLORebootCompletion + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" diff --git a/ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/aliases b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/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/ilo_redfish_config/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/tasks/main.yml new file mode 100644 index 000000000..30bfb4edd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_config/tasks/main.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 + +- name: Set NTP Servers + ilo_redfish_config: + category: Manager + command: SetNTPServers + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + attribute_name: StaticNTPServers + attribute_value: 1.2.3.4 + +- name: Set DNS Server + ilo_redfish_config: + category: Manager + command: SetDNSserver + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + attribute_name: DNSServers + attribute_value: 192.168.1.1 + +- name: Set Domain name + ilo_redfish_config: + category: Manager + command: SetDomainName + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + attribute_name: DomainName + attribute_value: tst.sgp.hp.mfg + +- name: Disable WINS Reg + ilo_redfish_config: + category: Manager + command: SetWINSReg + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + attribute_name: WINSRegistration + +- name: Set TimeZone + ilo_redfish_config: + category: Manager + command: SetTimeZone + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + attribute_name: TimeZone + attribute_value: Chennai diff --git a/ansible_collections/community/general/tests/integration/targets/ilo_redfish_info/aliases b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_info/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_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/ilo_redfish_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_info/tasks/main.yml new file mode 100644 index 000000000..0f80207ac --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ilo_redfish_info/tasks/main.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 + +- name: Get sessions + ilo_redfish_info: + category: Sessions + command: GetiLOSessions + baseuri: "{{ baseuri }}" + username: "{{ username }}" + password: "{{ password }}" + register: result_sessions diff --git a/ansible_collections/community/general/tests/integration/targets/influxdb_user/aliases b/ansible_collections/community/general/tests/integration/targets/influxdb_user/aliases new file mode 100644 index 000000000..8c1a7b329 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/influxdb_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/1 +destructive +disabled +skip/osx +skip/macos +skip/freebsd +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/influxdb_user/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/influxdb_user/meta/main.yml new file mode 100644 index 000000000..34b3a3a6c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/influxdb_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_influxdb diff --git a/ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/main.yml new file mode 100644 index 000000000..7da2f85e5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/main.yml @@ -0,0 +1,12 @@ +--- +#################################################################### +# 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 + +- include_tasks: tests.yml + when: ansible_distribution == 'Ubuntu' and ansible_distribution_release == 'trusty' diff --git a/ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/tests.yml b/ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/tests.yml new file mode 100644 index 000000000..be36ee691 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/influxdb_user/tasks/tests.yml @@ -0,0 +1,143 @@ +--- +# 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 influxdb python module + pip: name=influxdb + +- name: Test add admin user in check mode + block: + - name: Add admin user + influxdb_user: user_name=admin user_password=admin admin=yes + check_mode: true + register: add_admin_user + + - name: Check that admin user adding succeeds with a change + assert: + that: + - add_admin_user is changed + +- name: Test add admin user + block: + - name: Add admin user + influxdb_user: user_name=admin user_password=admin admin=yes + register: add_admin_user + + - name: Check that admin user adding succeeds with a change + assert: + that: + - add_admin_user is changed + +- name: Test add admin user idempotence + block: + - name: Add admin user + influxdb_user: user_name=admin user_password=admin admin=yes + register: add_admin_user + + - name: Check that admin user adding succeeds without a change + assert: + that: + - add_admin_user is not changed + +- name: Enable authentication and restart service + block: + - name: Enable authentication + lineinfile: + path: /etc/influxdb/influxdb.conf + regexp: 'auth-enabled =' + line: ' auth-enabled = true' + + - name: Restart InfluxDB service + service: name=influxdb state=restarted + +- name: Test add user in check mode when authentication enabled + block: + - name: Add user + influxdb_user: user_name=user user_password=user login_username=admin login_password=admin + check_mode: true + register: add_user_with_auth_enabled + + - name: Check that adding user with enabled authentication succeeds with a change + assert: + that: + - add_user_with_auth_enabled is changed + +- name: Test add user when authentication enabled + block: + - name: Add user + influxdb_user: user_name=user user_password=user login_username=admin login_password=admin + register: add_user_with_auth_enabled + + - name: Check that adding user with enabled authentication succeeds with a change + assert: + that: + - add_user_with_auth_enabled is changed + +- name: Test add user when authentication enabled idempotence + block: + - name: Add the same user + influxdb_user: user_name=user user_password=user login_username=admin login_password=admin + register: same_user + + - name: Check that adding same user succeeds without a change + assert: + that: + - same_user is not changed + +- name: Test change user password in check mode + block: + - name: Change user password + influxdb_user: user_name=user user_password=user2 login_username=admin login_password=admin + check_mode: true + register: change_password + + - name: Check that password changing succeeds with a change + assert: + that: + - change_password is changed + +- name: Test change user password + block: + - name: Change user password + influxdb_user: user_name=user user_password=user2 login_username=admin login_password=admin + register: change_password + + - name: Check that password changing succeeds with a change + assert: + that: + - change_password is changed + +- name: Test remove user in check mode + block: + - name: Remove user + influxdb_user: user_name=user state=absent login_username=admin login_password=admin + check_mode: true + register: remove_user + + - name: Check that removing user succeeds with a change + assert: + that: + - remove_user is changed + +- name: Test remove user + block: + - name: Remove user + influxdb_user: user_name=user state=absent login_username=admin login_password=admin + register: remove_user + + - name: Check that removing user succeeds with a change + assert: + that: + - remove_user is changed + +- name: Test remove user idempotence + block: + - name: Remove user + influxdb_user: user_name=user state=absent login_username=admin login_password=admin + register: remove_user + + - name: Check that removing user succeeds without a change + assert: + that: + - remove_user is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/aliases b/ansible_collections/community/general/tests/integration/targets/ini_file/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/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/ini_file/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/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/ini_file/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml new file mode 100644 index 000000000..11c5bf3b2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml @@ -0,0 +1,40 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# test code for ini_file plugins +# Copyright (c) 2017 Red Hat Inc. +# 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: record the output directory + set_fact: + output_file: "{{ remote_tmp_dir }}/foo.ini" + non_existing_file: "{{ remote_tmp_dir }}/bar.ini" + +- name: include tasks + block: + + - name: include tasks to perform basic tests + include_tasks: tests/00-basic.yml + + - name: reset output file + file: + path: "{{ output_file }}" + state: absent + + - name: include tasks to perform tests with parameter "value" + include_tasks: tests/01-value.yml + + - name: reset output file + file: + path: "{{ output_file }}" + state: absent + + - name: include tasks to perform tests with parameter "values" + include_tasks: tests/02-values.yml + + - name: include tasks to test regressions + include_tasks: tests/03-encoding.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 new file mode 100644 index 000000000..c619e937a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml @@ -0,0 +1,42 @@ +--- +# 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 + +## basiscs + +- name: test-basic 1 - specify both "value" and "values" and fail + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + value: lemonade + values: + - coke + - sprite + register: result_basic_1 + ignore_errors: true + +- name: test-basic 1 - verify error message + assert: + that: + - result_basic_1 is not changed + - result_basic_1 is failed + - "result_basic_1.msg == 'parameters are mutually exclusive: value|values'" + + +- name: test-basic 2 - set "create=no" on non-existing file and fail + ini_file: + path: "{{ non_existing_file }}" + section: food + create: false + value: banana + register: result_basic_2 + ignore_errors: true + +- name: test-basic 2 - verify error message + assert: + that: + - result_basic_2 is not changed + - result_basic_2 is failed + - 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/01-value.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/01-value.yml new file mode 100644 index 000000000..f95f166fe --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/01-value.yml @@ -0,0 +1,592 @@ +--- +# 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 value + +- name: test-value 1 - set "state=present" and "value=null" and "allow_no_value=false" and fail + ini_file: + path: "{{ output_file }}" + section: cars + option: audi + value: null + allow_no_value: false + register: result_value_1 + ignore_errors: true + +- name: test-value 1 - verify error message + assert: + that: + - result_value_1 is not changed + - result_value_1 is failed + - result_value_1.msg == "Parameter 'value(s)' must be defined if state=present and allow_no_value=False." + + +- name: test-value 2 - set "state=present" and omit "value" and "allow_no_value=false" and fail + ini_file: + path: "{{ output_file }}" + section: cars + option: audi + allow_no_value: false + register: result_value_2 + ignore_errors: true + +- name: test-value 2 - verify error message + assert: + that: + - result_value_2 is not changed + - result_value_2 is failed + - result_value_2.msg == "Parameter 'value(s)' must be defined if state=present and allow_no_value=False." + + +- name: test-value 3 - add "fav=lemonade" in section "[drinks]" in specified file + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + value: lemonade + register: result3 + +- name: test-value 3 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 3 - set expected content and get current ini file content + set_fact: + expected3: | + + [drinks] + fav = lemonade + content3: "{{ output_content.content | b64decode }}" + +- name: test-value 3 - Verify content of ini file is as expected and ini_file 'changed' is true + assert: + that: + - result3 is changed + - result3.msg == 'section and option added' + - content3 == expected3 + + +- name: test-value 4 - add "fav=lemonade" is in section "[drinks]" again + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + value: lemonade + register: result4 + +- name: test-value 4 - Ensure unchanged + assert: + that: + - result4 is not changed + - result4.msg == 'OK' + + +- name: test-value 5 - Ensure "beverage=coke" is in section "[drinks]" + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + value: coke + register: result5 + +- name: test-value 5 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 5 - set expected content and get current ini file content + set_fact: + expected5: | + + [drinks] + fav = lemonade + beverage = coke + content5: "{{ output_content.content | b64decode }}" + +- name: test-value 5 - assert 'changed' is true and content is OK + assert: + that: + - result5 is changed + - result5.msg == 'option added' + - content5 == expected5 + + +- name: test-value 6 - Remove option "beverage=coke" + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + state: absent + register: result6 + +- name: test-value 6 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 6 - set expected content and get current ini file content + set_fact: + expected6: | + + [drinks] + fav = lemonade + content6: "{{ output_content.content | b64decode }}" + +- name: test-value 6 - assert 'changed' is true and content is as expected + assert: + that: + - result6 is changed + - result6.msg == 'option changed' + - content6 == expected6 + + +- name: test-value 7 - remove section 'drinks' + ini_file: + path: "{{ output_file }}" + section: drinks + state: absent + register: result7 + +- name: test-value 7 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 7 - get current ini file content + set_fact: + content7: "{{ output_content.content | b64decode }}" + +- name: test-value 7 - assert 'changed' is true and content is empty + assert: + that: + - result7 is changed + - result7.msg == 'section removed' + - content7 == "\n" + + +# allow_no_value + +- name: test-value 8 - test allow_no_value + ini_file: + path: "{{ output_file }}" + section: mysqld + option: skip-name + allow_no_value: true + register: result8 + +- name: test-value 8 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 8 - set expected content and get current ini file content + set_fact: + content8: "{{ output_content.content | b64decode }}" + expected8: | + + [mysqld] + skip-name + +- name: test-value 8 - assert 'changed' is true and section and option added + assert: + that: + - result8 is changed + - result8.msg == 'section and option added' + - content8 == expected8 + + +- name: test-value 9 - test allow_no_value idempotency + ini_file: + path: "{{ output_file }}" + section: mysqld + option: skip-name + allow_no_value: true + register: result9 + +- name: test-value 9 - assert 'changed' is false + assert: + that: + - result9 is not changed + - result9.msg == 'OK' + + +- name: test-value 10 - test create empty section + ini_file: + path: "{{ output_file }}" + section: new_empty_section + allow_no_value: true + register: result10 + +- name: test-value 10 - assert 'changed' is true and section added + assert: + that: + - result10 is changed + - result10.msg == 'only section added' + + +- name: test-value 11 - test create empty section idempotency + ini_file: + path: "{{ output_file }}" + section: new_empty_section + allow_no_value: true + register: result11 + +- name: test-value 11 - assert 'changed' is false + assert: + that: + - result11 is not changed + - result11.msg == 'OK' + + +- name: test-value 12 - test remove empty section + ini_file: + state: absent + path: "{{ output_file }}" + section: new_empty_section + allow_no_value: true + +- name: test-value 12 - test allow_no_value with loop + ini_file: + path: "{{ output_file }}" + section: mysqld + option: "{{ item.o }}" + value: "{{ item.v | d(omit) }}" + allow_no_value: true + loop: + - { o: "skip-name-resolve" } + - { o: "max_connections", v: "500" } + +- name: test-value 12 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 12 - set expected content and get current ini file content + set_fact: + content12: "{{ output_content.content | b64decode }}" + expected12: | + + [mysqld] + skip-name + skip-name-resolve + max_connections = 500 + +- name: test-value 12 - Verify content of ini file is as expected + assert: + that: + - content12 == expected12 + + +- name: test-value 13 - change option with no value to option with value + ini_file: + path: "{{ output_file }}" + section: mysqld + option: skip-name + value: myvalue + register: result13 + +- name: test-value 13 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 13 - set expected content and get current ini file content + set_fact: + content13: "{{ output_content.content | b64decode }}" + expected13: | + + [mysqld] + skip-name = myvalue + skip-name-resolve + max_connections = 500 + +- name: test-value 13 - assert 'changed' and msg 'option changed' and content is as expected + assert: + that: + - result13 is changed + - result13.msg == 'option changed' + - content13 == expected13 + + +- name: test-value 14 - change option with value to option with no value + ini_file: + path: "{{ output_file }}" + section: mysqld + option: skip-name + allow_no_value: true + register: result14 + +- name: test-value 14 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 14 - set expected content and get current ini file content + set_fact: + content14: "{{ output_content.content | b64decode }}" + expected14: | + + [mysqld] + skip-name + skip-name-resolve + max_connections = 500 + +- name: test-value 14 - assert 'changed' is true and msg 'option changed' and content is as expected + assert: + that: + - result14 is changed + - result14.msg == 'option changed' + - content14 == expected14 + + +- name: test-value 15 - Remove option with no value + ini_file: + path: "{{ output_file }}" + section: mysqld + option: skip-name-resolve + state: absent + register: result15 + +- name: test-value 15 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 15 - set expected content and get current ini file content + set_fact: + content15: "{{ output_content.content | b64decode }}" + expected15: | + + [mysqld] + skip-name + max_connections = 500 + +- name: test-value 15 - assert 'changed' is true and msg 'option changed' and content is as expected + assert: + that: + - result15 is changed + - result15.msg == 'option changed' + - content15 == expected15 + + +- name: test-value 16 - Clean test file + copy: + content: "" + dest: "{{ output_file }}" + force: true + +- name: test-value 16 - Ensure "beverage=coke" is created within no section + ini_file: + section: + path: "{{ output_file }}" + option: beverage + value: coke + register: result16 + +- name: test-value 16 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 16 - set expected content and get current ini file content + set_fact: + expected16: |+ + beverage = coke + + content16: "{{ output_content.content | b64decode }}" + +- name: test-value 16 - assert 'changed' is true and content is OK (no section) + assert: + that: + - result16 is changed + - result16.msg == 'option added' + - content16 == expected16 + + +- name: test-value 17 - Ensure "beverage=coke" is modified as "beverage=water" within no section + ini_file: + path: "{{ output_file }}" + option: beverage + value: water + section: + register: result17 + +- name: test-value 17 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 17 - set expected content and get current ini file content + set_fact: + expected17: |+ + beverage = water + + content17: "{{ output_content.content | b64decode }}" + +- name: test-value 17 - assert 'changed' is true and content is OK (no section) + assert: + that: + - result17 is changed + - result17.msg == 'option changed' + - content17 == expected17 + + +- name: test-value 18 - remove option 'beverage' within no section + ini_file: + section: + path: "{{ output_file }}" + option: beverage + state: absent + register: result18 + +- name: test-value 18 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 18 - get current ini file content + set_fact: + content18: "{{ output_content.content | b64decode }}" + +- name: test-value 18 - assert 'changed' is true and option is removed (no section) + assert: + that: + - result18 is changed + - result18.msg == 'option changed' + - content18 == "\n" + + +- name: test-value 19 - Check add option without section before existing section + block: + - name: test-value 19 - Add option with section + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + value: water + - name: test-value 19 - Add option without section + ini_file: + path: "{{ output_file }}" + section: + option: like + value: tea + +- name: test-value 19 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 19 - set expected content and get current ini file content + set_fact: + expected19: | + like = tea + + [drinks] + beverage = water + content19: "{{ output_content.content | b64decode }}" + +- name: test-value 19 - Verify content of ini file is as expected + assert: + that: + - content19 == expected19 + + +- name: test-value 20 - Check add option with empty string value + block: + - name: test-value 20 - Remove drinks + ini_file: + path: "{{ output_file }}" + section: drinks + state: absent + - name: test-value 20 - Remove tea + ini_file: + path: "{{ output_file }}" + section: + option: like + value: tea + state: absent + # See https://github.com/ansible-collections/community.general/issues/3031 + - name: test-value 20 - Tests with empty strings + ini_file: + path: "{{ output_file }}" + section: "{{ item.section | d('extensions') }}" + option: "{{ item.option }}" + value: "" + allow_no_value: "{{ item.no_value | d(omit) }}" + loop: + - option: evolve + - option: regress + - section: foobar + option: foo + no_value: true + - option: improve + no_value: true + +- name: test-value 20 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 20 - set expected content and get current ini file content + set_fact: + expected20: |+ + + [extensions] + evolve = + regress = + improve = + [foobar] + foo = + content20: "{{ output_content.content | b64decode }}" + +- name: test-value 20 - Verify content of ini file is as expected + assert: + that: + - content20 == expected20 + + +- name: test-value 21 - Create starting ini file + copy: + # The content below is the following text file with BOM: + # [section1] + # var1=aaa + # var2=bbb + # [section2] + # var3=ccc + content: !!binary | + 77u/W3NlY3Rpb24xXQp2YXIxPWFhYQp2YXIyPWJiYgpbc2VjdGlvbjJdCnZhcjM9Y2NjCg== + dest: "{{ output_file }}" + +- name: test-value 21 - Test ini breakage + ini_file: + path: "{{ output_file }}" + section: section1 + option: var4 + value: 0 + register: result21 + +- name: test-value 21 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 21 - set expected content and get current ini file content + set_fact: + expected21: | + [section1] + var1=aaa + var2=bbb + var4 = 0 + [section2] + var3=ccc + content21: "{{ output_content.content | b64decode }}" + +- name: test-value 21 - Verify content of ini file is as expected + assert: + that: + - result21 is changed + - result21.msg == 'option added' + - content21 == expected21 diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/02-values.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/02-values.yml new file mode 100644 index 000000000..edfc93e42 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/02-values.yml @@ -0,0 +1,1023 @@ +--- +# 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 values + +- name: "test-values 1 - set 'state=present' and 'values=[]' and 'allow_no_value=false' and fail" + ini_file: + path: "{{ output_file }}" + section: cars + option: audi + values: [] + allow_no_value: false + register: result1 + ignore_errors: true + +- name: test-values 1 - verify error message + assert: + that: + - result1 is not changed + - result1 is failed + - result1.msg == "Parameter 'value(s)' must be defined if state=present and allow_no_value=False." + + +- name: "test-values 2 - set 'state=present' and omit 'values' and 'allow_no_value=false' and fail" + ini_file: + path: "{{ output_file }}" + section: cars + option: audi + allow_no_value: false + register: result2 + ignore_errors: true + +- name: test-values 2 - verify error message + assert: + that: + - result2 is not changed + - result2 is failed + - result2.msg == "Parameter 'value(s)' must be defined if state=present and allow_no_value=False." + + +- name: "test-values 3 - ensure 'fav=lemonade' and 'fav=cocktail' is 'present' in section '[drinks]' in specified file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + - cocktail + state: present + register: result3 + +- name: test-values 3 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 3 - set expected content and get current ini file content + set_fact: + expected3: | + + [drinks] + fav = lemonade + fav = cocktail + content3: "{{ output_content.content | b64decode }}" + +- name: test-values 3 - Verify content of ini file is as expected and ini_file 'changed' is true + assert: + that: + - result3 is changed + - result3.msg == 'section and option added' + - content3 == expected3 + + +- name: "test-values 4 - remove option 'fav=lemonade' from section '[drinks]' in specified file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + state: absent + exclusive: false + register: result4 + +- name: test-values 4 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 4 - set expected content and get current ini file content + set_fact: + expected4: | + + [drinks] + fav = cocktail + content4: "{{ output_content.content | b64decode }}" + +- name: test-values 4 - Verify content of ini file is as expected and ini_file 'changed' is true + assert: + that: + - result4 is changed + - result4.msg == 'option changed' + - content4 == expected4 + + +- name: "test-values 5 - add option 'fav=lemonade' in section '[drinks]' in specified file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + state: present + exclusive: false + register: result5 + +- name: test-values 5 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 5 - set expected content and get current ini file content + set_fact: + expected5: | + + [drinks] + fav = cocktail + fav = lemonade + content5: "{{ output_content.content | b64decode }}" + +- name: test-values 5 - Verify content of ini file is as expected and ini_file 'changed' is true + assert: + that: + - result5 is changed + - result5.msg == 'option added' + - content5 == expected5 + + +- name: "test-values 6 - ensure 'fav=lemonade' and 'fav=cocktail' is 'present' in section '[drinks]' and check for idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + - cocktail + state: present + register: result6 + +- name: test-values 6 - Ensure unchanged + assert: + that: + - result6 is not changed + - result6.msg == 'OK' + + +- name: "test-values 7 - ensure 'fav=cocktail' and 'fav=lemonade' (list reverse order) is 'present' in section '[drinks]' and check for idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - cocktail + - lemonade + state: present + register: result7 + +- name: test-values 7 - Ensure unchanged + assert: + that: + - result7 is not changed + - result7.msg == 'OK' + + +- name: "test-values 8 - add option 'fav=lemonade' in section '[drinks]' again and ensure idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + state: present + exclusive: false + register: result8 + +- name: test-values 8 - Ensure unchanged + assert: + that: + - result8 is not changed + - result8.msg == 'OK' + + +- name: "test-values 9 - ensure only 'fav=lemonade' is 'present' in section '[drinks]' in specified file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + state: present + register: result9 + +- name: test-values 9 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 9 - set expected content and get current ini file content + set_fact: + expected9: | + + [drinks] + fav = lemonade + content9: "{{ output_content.content | b64decode }}" + +- name: test-values 9 - Verify content of ini file is as expected and ini_file 'changed' is true + assert: + that: + - result9 is changed + - result9.msg == 'option changed' + - content9 == expected9 + + +- name: "test-values 10 - remove non-existent 'fav=cocktail' from section '[drinks]' in specified file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - cocktail + state: absent + register: result10 + +- name: test-values 10 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 10 - set expected content and get current ini file content + set_fact: + expected10: | + + [drinks] + content10: "{{ output_content.content | b64decode }}" + + +- name: test-values 10 - Ensure unchanged + assert: + that: + - result10 is changed + - result10.msg == 'option changed' + - content10 == expected10 + + +- name: "test-values 11 - Ensure 'fav=lemonade' and 'beverage=coke' is 'present' in section '[drinks]'" + block: + - name: "test-values 11 - resetting ini_fie: Ensure 'fav=lemonade' is 'present' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + state: present + - name: "test-values 11 - Ensure 'beverage=coke' is 'present' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + values: + - coke + state: present + register: result11 + +- name: test-values 11 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 11 - set expected content and get current ini file content + set_fact: + expected11: | + + [drinks] + fav = lemonade + beverage = coke + content11: "{{ output_content.content | b64decode }}" + +- name: test-values 11 - assert 'changed' is true and content is OK + assert: + that: + - result11 is changed + - result11.msg == 'option added' + - content11 == expected11 + + +- name: "test-values 12 - add option 'fav=lemonade' in section '[drinks]' again and ensure idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + state: present + exclusive: false + register: result12 + +- name: test-values 12 - Ensure unchanged + assert: + that: + - result12 is not changed + - result12.msg == 'OK' + + +- name: "test-values 13 - add option 'fav=cocktail' in section '[drinks]' in specified file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - cocktail + state: present + exclusive: false + register: result13 + +- name: test-values 13 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 13 - set expected content and get current ini file content + set_fact: + expected13: | + + [drinks] + fav = lemonade + beverage = coke + fav = cocktail + content13: "{{ output_content.content | b64decode }}" + +- name: test-values 13 - Verify content of ini file is as expected and ini_file 'changed' is true + assert: + that: + - result13 is changed + - result13.msg == 'option added' + - content13 == expected13 + + +- name: "test-values 14 - Ensure 'refreshment=[water, juice, soft drink]' is 'present' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: refreshment + values: + - water + - juice + - soft drink + state: present + register: result14 + +- name: test-values 14 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 14 - set expected content and get current ini file content + set_fact: + expected14: | + + [drinks] + fav = lemonade + beverage = coke + fav = cocktail + refreshment = water + refreshment = juice + refreshment = soft drink + content14: "{{ output_content.content | b64decode }}" + +- name: test-values 14 - assert 'changed' is true and content is OK + assert: + that: + - result14 is changed + - result14.msg == 'option added' + - content14 == expected14 + + +- name: "test-values 15 - ensure 'fav=lemonade' and 'fav=cocktail' is 'present' in section '[drinks]' and check for idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - lemonade + - cocktail + state: present + register: result15 + +- name: test-values 15 - Ensure unchanged + assert: + that: + - result15 is not changed + - result15.msg == 'OK' + + +- name: "test-values 16 - ensure 'fav=cocktail' and 'fav=lemonade' (list reverse order) is 'present' in section '[drinks]' and check for idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - cocktail + - lemonade + state: present + register: result16 + +- name: test-values 16 - Ensure unchanged + assert: + that: + - result16 is not changed + - result16.msg == 'OK' + + +- name: "test-values 17 - Ensure option 'refreshment' is 'absent' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: refreshment + state: absent + register: result17 + +- name: test-values 17 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 17 - set expected content and get current ini file content + set_fact: + expected17: | + + [drinks] + fav = lemonade + beverage = coke + fav = cocktail + content17: "{{ output_content.content | b64decode }}" + +- name: test-values 17 - assert 'changed' is true and content is as expected + assert: + that: + - result17 is changed + - result17.msg == 'option changed' + - content17 == expected17 + + +- name: "test-values 18 - Ensure 'beverage=coke' is 'absent' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + state: absent + register: result18 + +- name: test-values 18 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 18 - set expected content and get current ini file content + set_fact: + expected18: | + + [drinks] + fav = lemonade + fav = cocktail + content18: "{{ output_content.content | b64decode }}" + +- name: test-values 18 - assert 'changed' is true and content is as expected + assert: + that: + - result18 is changed + - result18.msg == 'option changed' + - content18 == expected18 + + +- name: "test-values 19 - Ensure non-existent 'beverage=coke' is 'absent' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + values: + - coke + state: absent + register: result19 + +- name: test-values 19 - Ensure unchanged + assert: + that: + - result19 is not changed + - result19.msg == 'OK' + + +- name: test-values 20 - remove section 'drinks' + ini_file: + path: "{{ output_file }}" + section: drinks + state: absent + register: result20 + +- name: test-values 20 - remove section 'drinks' again to ensure idempotency" + ini_file: + path: "{{ output_file }}" + section: drinks + state: absent + register: result20_remove_again + +- name: test-values 20 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 20 - get current ini file content + set_fact: + content20: "{{ output_content.content | b64decode }}" + +- name: test-values 20 - assert 'changed' is true and content is empty + assert: + that: + - result20 is changed + - result20_remove_again is not changed + - result20.msg == 'section removed' + - content20 == "\n" + + +- name: "test-values 21 - Ensure 'refreshment=[water, juice, soft drink, juice]' (duplicates removed) is 'present' in section '[drinks]'" + ini_file: + path: "{{ output_file }}" + section: drinks + option: refreshment + values: + - water + - juice + - soft drink + - juice + state: present + register: result21 + +- name: test-values 21 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 21 - set expected content and get current ini file content + set_fact: + expected21: | + + [drinks] + refreshment = water + refreshment = juice + refreshment = soft drink + content21: "{{ output_content.content | b64decode }}" + +- name: test-values 21 - assert 'changed' is true and content is OK + assert: + that: + - result21 is changed + - result21.msg == 'section and option added' + - content21 == expected21 + + +- name: test-values 22 - Create starting ini file + copy: + content: | + + # Some comment to test + [mysqld] + connect_timeout = 300 + max_connections = 1000 + [section1] + var1 = aaa + # comment in section + # var2 = some value + # comment after section + + [section2] + var3 = ccc + # comment after section + dest: "{{ output_file }}" + +- name: "test-values 22 - Ensure 'skip-name' with 'allow_no_value' is 'present' in section '[mysqld]' test allow_no_value" + ini_file: + path: "{{ output_file }}" + section: mysqld + option: skip-name + allow_no_value: true + state: present + register: result22 + +- name: test-values 22 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 22 - set expected content and get current ini file content + set_fact: + expected22: | + + # Some comment to test + [mysqld] + connect_timeout = 300 + max_connections = 1000 + skip-name + [section1] + var1 = aaa + # comment in section + # var2 = some value + # comment after section + + [section2] + var3 = ccc + # comment after section + content22: "{{ output_content.content | b64decode }}" + +- name: test-values 22 - assert 'changed' is true and content is OK and option added + assert: + that: + - result22 is changed + - result22.msg == 'option added' + - content22 == expected22 + + +- name: "test-values 23 - Ensure 'var2=foo' is 'present' in section '[section1]', replacing commented option 'var2=some value'" + ini_file: + path: "{{ output_file }}" + section: section1 + option: var2 + values: + - foo + state: present + register: result23 + +- name: test-values 23 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 23 - set expected content and get current ini file content + set_fact: + expected23: | + + # Some comment to test + [mysqld] + connect_timeout = 300 + max_connections = 1000 + skip-name + [section1] + var1 = aaa + # comment in section + var2 = foo + # comment after section + + [section2] + var3 = ccc + # comment after section + content23: "{{ output_content.content | b64decode }}" + +- name: test-values 23 - assert 'changed' and msg 'option changed' and content is as expected + assert: + that: + - result23 is changed + - result23.msg == 'option changed' + - content23 == expected23 + + +- name: "test-values 24 - Ensure 'var2=[foo, foobar]' is 'present' in section '[section1]'" + ini_file: + path: "{{ output_file }}" + section: section1 + option: var2 + values: + - foo + - foobar + state: present + register: result24 + +- name: test-values 24 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 24 - set expected content and get current ini file content + set_fact: + expected24: | + + # Some comment to test + [mysqld] + connect_timeout = 300 + max_connections = 1000 + skip-name + [section1] + var1 = aaa + # comment in section + var2 = foo + var2 = foobar + # comment after section + + [section2] + var3 = ccc + # comment after section + content24: "{{ output_content.content | b64decode }}" + +- name: test-values 24 - assert 'changed' and msg 'option added' and content is as expected + assert: + that: + - result24 is changed + - result24.msg == 'option added' + - content24 == expected24 + + +- name: test-values 25 - Clean test file + copy: + content: "" + dest: "{{ output_file }}" + force: true + +- name: "test-values 25 - Ensure 'beverage=[coke, pepsi]' is created within no section" + ini_file: + section: + path: "{{ output_file }}" + option: beverage + values: + - coke + - pepsi + state: present + register: result25 + +- name: test-values 25 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 25 - set expected content and get current ini file content + set_fact: + expected25: |+ + beverage = coke + beverage = pepsi + + content25: "{{ output_content.content | b64decode }}" + +- name: test-values 25 - assert 'changed' is true and content is OK (no section) + assert: + that: + - result25 is changed + - result25.msg == 'option added' + - content25 == expected25 + + +- name: "test-values 26 - Ensure 'beverage=coke' and 'beverage=pepsi' are modified within no section" + ini_file: + path: "{{ output_file }}" + option: beverage + values: + - water + - orange juice + section: + state: present + exclusive: true + register: result26 + +- name: test-values 26 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 26 - set expected content and get current ini file content + set_fact: + expected26: |+ + beverage = water + beverage = orange juice + + content26: "{{ output_content.content | b64decode }}" + +- name: test-values 26 - assert 'changed' is true and content is OK (no section) + assert: + that: + - result26 is changed + - result26.msg == 'option changed' + - content26 == expected26 + + +- name: "test-values 27 - ensure option 'beverage' is 'absent' within no section" + ini_file: + section: + path: "{{ output_file }}" + option: beverage + state: absent + register: result27 + +- name: test-values 27 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 27 - get current ini file content + set_fact: + content27: "{{ output_content.content | b64decode }}" + +- name: test-values 27 - assert changed (no section) + assert: + that: + - result27 is changed + - result27.msg == 'option changed' + - content27 == "\n" + + +- name: "test-values 28 - Ensure option 'present' without section before existing section" + block: + - name: test-values 28 - ensure option present within section + ini_file: + path: "{{ output_file }}" + section: drinks + option: beverage + values: + - water + - orange juice + state: present + + - name: test-values 28 - ensure option present without section + ini_file: + path: "{{ output_file }}" + section: + option: like + values: + - tea + - coffee + state: present + +- name: test-values 28 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-values 28 - set expected content and get current ini file content + set_fact: + expected28: | + like = tea + like = coffee + + [drinks] + beverage = water + beverage = orange juice + content28: "{{ output_content.content | b64decode }}" + +- name: test-values 28 - Verify content of ini file is as expected + assert: + that: + - content28 == expected28 + + +- name: test-value 29 - Create starting ini file + copy: + content: | + [drinks] + fav = cocktail + beverage = water + fav = lemonade + beverage = orange juice + dest: "{{ output_file }}" + +- name: "test-value 29 - Test 'state=absent' with 'exclusive=true' with multiple options in ini_file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - cocktail + state: absent + register: result29 + +- name: test-value 29 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 29 - set expected content and get current ini file content + set_fact: + expected29: | + [drinks] + beverage = water + beverage = orange juice + content29: "{{ output_content.content | b64decode }}" + +- name: test-value 29 - Verify content of ini file is as expected + assert: + that: + - result29 is changed + - result29.msg == 'option changed' + - content29 == expected29 + + +- name: test-value 30 - Create starting ini file + copy: + content: | + [drinks] + fav = cocktail + beverage = water + fav = lemonade + beverage = orange juice + dest: "{{ output_file }}" + +- name: "test-value 30 - Test 'state=absent' with 'exclusive=false' with multiple options in ini_file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + values: + - cocktail + state: absent + exclusive: false + register: result30 + +- name: test-value 30 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 30 - set expected content and get current ini file content + set_fact: + expected30: | + [drinks] + beverage = water + fav = lemonade + beverage = orange juice + content30: "{{ output_content.content | b64decode }}" + +- name: test-value 30 - Verify content of ini file is as expected + assert: + that: + - result30 is changed + - result30.msg == 'option changed' + - content30 == expected30 + + +- name: test-value 31 - Create starting ini file + copy: + content: | + [drinks] + fav = cocktail + beverage = water + fav = lemonade + beverage = orange juice + dest: "{{ output_file }}" + +- name: "test-value 31 - Test 'state=absent' with 'exclusive=true' and no value given with multiple options in ini_file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + state: absent + register: result31 + +- name: test-value 31 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 31 - set expected content and get current ini file content + set_fact: + expected31: | + [drinks] + beverage = water + beverage = orange juice + content31: "{{ output_content.content | b64decode }}" + +- name: test-value 31 - Verify content of ini file is as expected + assert: + that: + - result31 is changed + - result31.msg == 'option changed' + - content31 == expected31 + + +- name: test-value 32 - Create starting ini file + copy: + content: | + [drinks] + fav = cocktail + beverage = water + fav = lemonade + beverage = orange juice + dest: "{{ output_file }}" + +- name: "test-value 32 - Test 'state=absent' with 'exclusive=false' and no value given with multiple options in ini_file" + ini_file: + path: "{{ output_file }}" + section: drinks + option: fav + state: absent + exclusive: false + register: result32 + diff: true + +- name: test-value 32 - read content from output file + slurp: + src: "{{ output_file }}" + register: output_content + +- name: test-value 32 - set expected content and get current ini file content + set_fact: + expected32: | + [drinks] + fav = cocktail + beverage = water + fav = lemonade + beverage = orange juice + content32: "{{ output_content.content | b64decode }}" + +- name: test-value 32 - Verify content of ini file is as expected + assert: + that: + - result32 is not changed + - result32.msg == 'OK' + - content32 == expected32 diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/03-encoding.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/03-encoding.yml new file mode 100644 index 000000000..555dd576c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/03-encoding.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 + +# Regression test for https://github.com/ansible-collections/community.general/pull/2578#issuecomment-868092282 +- name: Create UTF-8 test file + copy: + content: !!binary | + W2FwcDptYWluXQphdmFpbGFibGVfbGFuZ3VhZ2VzID0gZW4gZnIgZXMgZGUgcHQgamEgbHQgemhf + VFcgaWQgZGEgcHRfQlIgcnUgc2wgaXQgbmxfTkwgdWsgdGEgc2kgY3MgbmIgaHUKIyBGdWxsIGxh + bmd1YWdlIG5hbWVzIGluIG5hdGl2ZSBsYW5ndWFnZSAoY29tbWEgc2VwYXJhdGVkKQphdmFpbGFi + bGVfbGFuZ3VhZ2VzX2Z1bGwgPSBFbmdsaXNoLCBGcmFuw6dhaXMsIEVzcGHDsW9sLCBEZXV0c2No + LCBQb3J0dWd1w6pzLCDml6XmnKzoqp4sIExpZXR1dm9zLCDkuK3mlocsIEluZG9uZXNpYSwgRGFu + c2ssIFBvcnR1Z3XDqnMgKEJyYXNpbCksINCg0YPRgdGB0LrQuNC5LCBTbG92ZW7FocSNaW5hLCBJ + dGFsaWFubywgTmVkZXJsYW5kcywg0KPQutGA0LDRl9C90YHRjNC60LAsIOCupOCuruCuv+CutOCv + jSwg4LeD4LeS4LaC4LeE4La9LCDEjGVza3ksIEJva23DpWwsIE1hZ3lhcgo= + dest: '{{ output_file }}' +- name: Add entries + ini_file: + section: "{{ item.section }}" + option: "{{ item.option }}" + value: "{{ item.value }}" + path: '{{ output_file }}' + create: true + loop: + - section: app:main + option: sqlalchemy.url + value: postgresql://app:secret@database/app + - section: handler_filelog + option: args + value: (sys.stderr,) + - section: handler_filelog + option: class + value: StreamHandler + - section: handler_exc_handler + option: args + value: (sys.stderr,) + - section: båz + option: fföø + value: ḃâŗ + - section: båz + option: fföø + value: bar diff --git a/ansible_collections/community/general/tests/integration/targets/interfaces_file/aliases b/ansible_collections/community/general/tests/integration/targets/interfaces_file/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/interfaces_file/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/interfaces_file/files/interfaces_ff b/ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff new file mode 100644 index 000000000..c7f0452df --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff @@ -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 + +iface eno1 inet static + address 1.2.3.4 + netmask 255.255.255.0 + gateway 1.2.3.1 + up route add -net 1.2.3.4 netmask 255.255.255.0 gw 1.2.3.1 eno1 + up ip addr add 4.3.2.1/32 dev eno1 + down ip addr add 4.3.2.1/32 dev eno1 diff --git a/ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff_3841 b/ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff_3841 new file mode 100644 index 000000000..9f47879c5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/interfaces_file/files/interfaces_ff_3841 @@ -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 + +iface eth0 inet static + address 1.2.3.4 + netmask 255.255.255.0 + gateway 1.2.3.1 + up route add -net 1.2.3.4 netmask 255.255.255.0 gw 1.2.3.1 eth0 + up ip addr add 4.3.2.1/32 dev eth0 + down ip addr add 4.3.2.1/32 dev eth0 diff --git a/ansible_collections/community/general/tests/integration/targets/interfaces_file/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/interfaces_file/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/interfaces_file/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/interfaces_file/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml new file mode 100644 index 000000000..918a32331 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml @@ -0,0 +1,67 @@ +--- +# 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: + set_fact: + interfaces_testfile: '{{ remote_tmp_dir }}/interfaces' + interfaces_testfile_3841: '{{ remote_tmp_dir }}/interfaces_3841' + +- name: Copy interfaces file + copy: + src: 'files/interfaces_ff' + dest: '{{ interfaces_testfile }}' + +- name: Change IP address to 1.2.3.5 + community.general.interfaces_file: + dest: "{{ interfaces_testfile }}" + iface: eno1 + option: address + value: 1.2.3.5 + register: ifile_1 + +- assert: + that: + - ifile_1 is changed + +- name: Change IP address to 1.2.3.5 again + community.general.interfaces_file: + dest: "{{ interfaces_testfile }}" + iface: eno1 + option: address + value: 1.2.3.5 + register: ifile_2 + +- assert: + that: + - ifile_2 is not changed + +- name: 3841 - copy interfaces file + copy: + src: 'files/interfaces_ff_3841' + dest: '{{ interfaces_testfile_3841 }}' + +- name: 3841 - floating_ip_interface_up_ip 2a01:a:b:c::1/64 dev eth0 + interfaces_file: + option: up + iface: eth0 + dest: "{{ interfaces_testfile_3841 }}" + value: 'ip addr add 2a01:a:b:c::1/64 dev eth0' + state: present + register: ifile_3841_a + +- name: 3841 - floating_ip_interface_up_ip 2a01:a:b:c::1/64 dev eth0 (again) + interfaces_file: + option: up + iface: eth0 + dest: "{{ interfaces_testfile_3841 }}" + value: 'ip addr add 2a01:a:b:c::1/64 dev eth0' + state: present + register: ifile_3841_b + +- name: 3841 - check assertions + assert: + that: + - ifile_3841_a is changed + - ifile_3841_b is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/ipify_facts/aliases b/ansible_collections/community/general/tests/integration/targets/ipify_facts/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ipify_facts/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/ipify_facts/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ipify_facts/tasks/main.yml new file mode 100644 index 000000000..78e44e946 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ipify_facts/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 ipify_facts +# Copyright (c) 2017, Abhijeet Kasurde +# 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: var=ansible_distribution +- debug: var=ansible_distribution_version + +- set_fact: + validate_certs: false + when: (ansible_distribution == "MacOSX" and ansible_distribution_version == "10.11.1") + +- name: get information about current IP using ipify facts + ipify_facts: + timeout: 30 + validate_certs: "{{ validate_certs }}" + register: external_ip + until: external_ip is successful + retries: 5 + delay: 10 + +- name: check if task was successful + assert: + that: + - external_ip is not changed + - external_ip.ansible_facts is defined + - external_ip.ansible_facts.ipify_public_ip is defined diff --git a/ansible_collections/community/general/tests/integration/targets/ipify_facts/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/ipify_facts/vars/main.yml new file mode 100644 index 000000000..3a47dfea8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ipify_facts/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 + +validate_certs: true diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/aliases b/ansible_collections/community/general/tests/integration/targets/iptables_state/aliases new file mode 100644 index 000000000..5a02a630b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/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 +skip/docker # kernel modules not loadable +skip/freebsd # no iptables/netfilter (Linux specific) +skip/osx # no iptables/netfilter (Linux specific) +skip/macos # no iptables/netfilter (Linux specific) +skip/aix # no iptables/netfilter (Linux specific) diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/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/iptables_state/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml new file mode 100644 index 000000000..a74e74df4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml @@ -0,0 +1,38 @@ +--- +#################################################################### +# 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: ensure iptables package is installed + package: + name: + - iptables + become: true + + +- name: include tasks + vars: + iptables_saved: "/tmp/test_iptables_state.saved" + iptables_tests: "/tmp/test_iptables_state.tests" + + block: + - name: include tasks to perform basic tests (check_mode, async, idempotency) + include_tasks: tests/00-basic.yml + + - name: include tasks to test tables handling + include_tasks: tests/01-tables.yml + when: + - xtables_lock is undefined + + - name: include tasks to test rollbacks + include_tasks: tests/10-rollback.yml + when: + - xtables_lock is undefined + - ansible_connection in ['ssh', 'paramiko', 'smart'] + + become: true diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/00-basic.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/00-basic.yml new file mode 100644 index 000000000..7b366edce --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/00-basic.yml @@ -0,0 +1,320 @@ +--- +# 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 our next backup is not there (file)" + file: + path: "{{ iptables_saved }}" + state: absent + +- name: "ensure our next rule is not there (iptables)" + iptables: + chain: OUTPUT + jump: ACCEPT + state: absent + + +# +# Basic checks about invalid param/value handling. +# +- name: "trigger error about invalid param" + iptables_state: + name: foobar + register: iptables_state + ignore_errors: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is failed + - iptables_state.msg is match("Invalid options") + quiet: true + + + +- name: "trigger error about missing param 'state'" + iptables_state: + path: foobar + register: iptables_state + ignore_errors: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is failed + - iptables_state.msg is match("missing required arguments") + quiet: true + + + +- name: "trigger error about missing param 'path'" + iptables_state: + state: saved + register: iptables_state + ignore_errors: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is failed + - iptables_state.msg is match("missing required arguments") + quiet: true + + + +- name: "trigger error about invalid value for param 'state'" + iptables_state: + path: foobar + state: present + register: iptables_state + ignore_errors: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is failed + - iptables_state.msg is match("value of state must be one of") + quiet: true + + +# +# Play with the current state first. We will create a file to store it in, but +# no more. These tests are for: +# - idempotency +# - check_mode +# +- name: "save state (check_mode, must report a change)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + register: iptables_state + check_mode: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + - iptables_state.initial_state == iptables_state.saved + quiet: true + + + +- name: "save state (must report a change)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + register: iptables_state + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + - iptables_state.initial_state == iptables_state.saved + quiet: true + + + +- name: "save state (idempotency, must NOT report a change)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + register: iptables_state + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + - iptables_state.initial_state == iptables_state.saved + quiet: true + + + +- name: "save state (check_mode, must NOT report a change)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + register: iptables_state + check_mode: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + - iptables_state.initial_state == iptables_state.saved + quiet: true + + + +# We begin with 'state=restored' by restoring the current state on itself. +# This at least ensures the file produced with state=saved is suitable for +# state=restored. + +- name: "state=restored check_mode=true changed=false" + block: + - name: "restore state (check_mode, must NOT report a change, no warning)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + check_mode: true + + - name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + - iptables_state.initial_state == iptables_state.restored + quiet: true + + rescue: + - name: "assert that results are not as expected for only one reason (xtables lock)" + assert: + that: + - iptables_state is failed + - iptables_state.stderr is search('xtables lock') + quiet: true + register: xtables_lock + + + +- name: "state=restored changed=false" + block: + - name: "restore state (must NOT report a change, warning about rollback & async)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + + - name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + - iptables_state.initial_state == iptables_state.restored + quiet: true + + rescue: + - name: "assert that results are not as expected for only one reason (xtables lock)" + assert: + that: + - iptables_state is failed + - iptables_state.stderr is search('xtables lock') + quiet: true + register: xtables_lock + + + +- name: "change iptables state (iptables)" + iptables: + chain: OUTPUT + jump: ACCEPT + + + +- name: "state=restored changed=true" + block: + - name: "restore state (check_mode, must report a change)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + check_mode: true + + - name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + - iptables_state.initial_state != iptables_state.restored + quiet: true + + rescue: + - name: "assert that results are not as expected for only one reason (xtables lock)" + assert: + that: + - iptables_state is failed + - iptables_state.stderr is search('xtables lock') + quiet: true + register: xtables_lock + + + +- name: "state=restored changed=true" + block: + - name: "restore state (must report a change, async, no warning)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + + - name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + - iptables_state.initial_state != iptables_state.restored + - iptables_state.applied + quiet: true + + rescue: + - name: "assert that results are not as expected for only one reason (xtables lock)" + assert: + that: + - iptables_state is failed + - iptables_state.stderr is search('xtables lock') + quiet: true + register: xtables_lock + + + +- name: "state=restored changed=false" + block: + - name: "restore state (must NOT report a change, async, no warning)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + + - name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + - iptables_state.initial_state == iptables_state.restored + quiet: true + + rescue: + - name: "assert that results are not as expected for only one reason (xtables lock)" + assert: + that: + - iptables_state is failed + - iptables_state.stderr is search('xtables lock') + quiet: true + register: xtables_lock + + + +- name: "state=restored changed=false" + block: + - name: "restore state (check_mode=yes, must NOT report a change, no warning)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + check_mode: true + + - name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + - iptables_state.initial_state == iptables_state.restored + quiet: true + + rescue: + - name: "assert that results are not as expected for only one reason (xtables lock)" + assert: + that: + - iptables_state is failed + - iptables_state.stderr is search('xtables lock') + quiet: true + register: xtables_lock diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml new file mode 100644 index 000000000..8a9869c43 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/01-tables.yml @@ -0,0 +1,294 @@ +--- +# 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 our next rule is not there (iptables)" + iptables: + table: nat + chain: INPUT + jump: ACCEPT + state: absent + +- name: "get state (table filter)" + iptables_state: + table: filter + state: saved + path: "{{ iptables_saved }}" + register: iptables_state + changed_when: false + check_mode: true + +- name: "assert that results are as expected" + assert: + that: + - "'*filter' in iptables_state.initial_state" + - iptables_state.tables.filter is defined + - iptables_state.tables.nat is undefined + quiet: true + + + +- name: "get state (table nat)" + iptables_state: + table: nat + state: saved + path: "{{ iptables_saved }}" + register: iptables_state + changed_when: false + check_mode: true + +- name: "assert that results are as expected" + assert: + that: + - "'*nat' in iptables_state.initial_state" + - "'*filter' in iptables_state.initial_state" + - iptables_state.tables.nat is defined + - iptables_state.tables.filter is undefined + quiet: true + + + +- name: "save state (table filter)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + table: filter + register: iptables_state + +- name: "assert that results are as expected" + assert: + that: + - "'*filter' in iptables_state.initial_state" + - "'*filter' in iptables_state.saved" + - "'*nat' in iptables_state.initial_state" + - "'*nat' not in iptables_state.saved" + - iptables_state.tables.filter is defined + - iptables_state.tables.nat is undefined + quiet: true + + + +- name: "save state (table nat)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + table: nat + register: iptables_state + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + - "'*nat' in iptables_state.initial_state" + - "'*nat' in iptables_state.saved" + - "'*filter' in iptables_state.initial_state" + - "'*filter' not in iptables_state.saved" + - iptables_state.tables.nat is defined + - iptables_state.tables.filter is undefined + quiet: true + + + +- name: "save state (any table)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + register: iptables_state + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + - "'*filter' in iptables_state.initial_state" + - "'*filter' in iptables_state.saved" + - "'*nat' in iptables_state.initial_state" + - "'*nat' in iptables_state.saved" + - iptables_state.tables.filter is defined + - iptables_state.tables.nat is defined + quiet: true + + + +- name: "restore state (table nat, must NOT report a change, no warning)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + table: nat + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + +- name: "assert that results are as expected" + assert: + that: + - "'*nat' in iptables_state.initial_state" + - "'*nat' in iptables_state.restored" + - "'*filter' in iptables_state.initial_state" + - "'*filter' not in iptables_state.restored" + - iptables_state.tables.nat is defined + - iptables_state.tables.filter is undefined + - iptables_state is not changed + quiet: true + + + +- name: "change NAT table (iptables)" + iptables: + table: nat + chain: INPUT + jump: ACCEPT + state: present + + + +- name: "restore state (table nat, must report a change, no warning)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + table: nat + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + +- name: "assert that results are as expected" + assert: + that: + - "'*nat' in iptables_state.initial_state" + - "'*nat' in iptables_state.restored" + - "'*filter' in iptables_state.initial_state" + - "'*filter' not in iptables_state.restored" + - iptables_state.tables.nat is defined + - "'-A INPUT -j ACCEPT' in iptables_state.tables.nat" + - "'-A INPUT -j ACCEPT' not in iptables_state.restored" + - iptables_state.tables.filter is undefined + - iptables_state is changed + quiet: true + + + +- name: "get raw and mangle tables states" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + table: "{{ item }}" + loop: + - raw + - mangle + changed_when: false + check_mode: true + + + +- name: "save state (any table)" + iptables_state: + path: "{{ iptables_saved }}" + state: saved + register: iptables_state + +- name: "assert that results are as expected" + assert: + that: + - "'filter' in iptables_state.tables" + - "'*filter' in iptables_state.saved" + - "'mangle' in iptables_state.tables" + - "'*mangle' in iptables_state.saved" + - "'nat' in iptables_state.tables" + - "'*nat' in iptables_state.saved" + - "'raw' in iptables_state.tables" + - "'*raw' in iptables_state.saved" + quiet: true + + + +- name: "save filter table into a test file" + iptables_state: + path: "{{ iptables_tests }}" + table: filter + state: saved + +- name: "add a table header in comments (# *mangle)" + lineinfile: + path: "{{ iptables_tests }}" + line: "# *mangle" + + + +- name: "restore state (table filter, must NOT report a change, no warning)" + iptables_state: + path: "{{ iptables_tests }}" + table: filter + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + +- name: "assert that results are as expected" + assert: + that: + - "'*filter' in iptables_state.initial_state" + - "'*mangle' in iptables_state.initial_state" + - "'*nat' in iptables_state.initial_state" + - "'*raw' in iptables_state.initial_state" + - "'filter' in iptables_state.tables" + - "'mangle' not in iptables_state.tables" + - "'nat' not in iptables_state.tables" + - "'raw' not in iptables_state.tables" + - "'*filter' in iptables_state.restored" + - "'*mangle' not in iptables_state.restored" + - "'*nat' not in iptables_state.restored" + - "'*raw' not in iptables_state.restored" + - iptables_state is not changed + quiet: true + + + +- name: "restore state (any table, must NOT report a change, no warning)" + iptables_state: + path: "{{ iptables_tests }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + +- name: "assert that results are as expected" + assert: + that: + - "'*filter' in iptables_state.initial_state" + - "'*mangle' in iptables_state.initial_state" + - "'*nat' in iptables_state.initial_state" + - "'*raw' in iptables_state.initial_state" + - "'filter' in iptables_state.tables" + - "'mangle' in iptables_state.tables" + - "'nat' in iptables_state.tables" + - "'raw' in iptables_state.tables" + - "'*filter' in iptables_state.restored" + - "'*mangle' in iptables_state.restored" + - "'*nat' in iptables_state.restored" + - "'*raw' in iptables_state.restored" + - iptables_state is not changed + quiet: true + + + +- name: "restore state (table mangle, must fail, no warning)" + iptables_state: + path: "{{ iptables_tests }}" + table: mangle + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + ignore_errors: true + +- name: "explain expected failure" + assert: + that: + - iptables_state is failed + - "iptables_state.msg == 'Table mangle to restore not defined in {{ iptables_tests }}'" + success_msg: >- + The previous error has been triggered by trying to restore a table + that is missing in the file provided to iptables-restore. + fail_msg: >- + The previous task should have failed due to a missing table (mangle) + in the file to restore iptables state from. diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/10-rollback.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/10-rollback.yml new file mode 100644 index 000000000..53fdd3ca0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/10-rollback.yml @@ -0,0 +1,203 @@ +--- +# 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 blocking ruleset with a DROP policy" + copy: + dest: "{{ iptables_tests }}" + content: | + *filter + :INPUT DROP + COMMIT + + + +- name: "restore state from the test file (check_mode, must report a change)" + iptables_state: + path: "{{ iptables_tests }}" + state: restored + register: iptables_state + check_mode: true + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is changed + + + +- name: "fail to restore state from the test file" + block: + - name: "restore state from the test file (bad policies, expected error -> rollback)" + iptables_state: + path: "{{ iptables_tests }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + + rescue: + - name: "explain expected failure" + assert: + that: + - iptables_state is not changed + - not iptables_state.applied + success_msg: >- + The previous error has been triggered to test the rollback. If you + are there, it means that 1) connection has been lost right after the + bad rules have been restored; 2) a rollback happened, so the bad + rules are not applied, finally; 3) module failed because it didn't + reach the wanted state, but at least host is not lost !!! + fail_msg: >- + The previous error has been triggered but its results are not as + expected. + +- name: "check that the expected failure happened" + assert: + that: + - iptables_state is failed + + + +- name: "fail to restore state from the test file (again)" + block: + - name: "try again, with a higher timeout (bad policies, same expected error)" + iptables_state: + path: "{{ iptables_tests }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + vars: + ansible_timeout: "{{ max_delay | d(300) }}" + + rescue: + - name: "explain expected failure" + assert: + that: + - iptables_state is not changed + - not iptables_state.applied + success_msg: >- + The previous error has been triggered to test the rollback. If you + are there, it means that 1) connection has been lost right after the + bad rules have been restored; 2) a rollback happened, so the bad + rules are not applied, finally; 3) module failed because it didn't + reach the wanted state, but at least host is not lost !!! + fail_msg: >- + The previous error has been triggered but its results are not as + expected. + +- name: "check that the expected failure happened" + assert: + that: + - iptables_state is failed + + + +- name: "restore state from backup (must NOT report a change)" + iptables_state: + path: "{{ iptables_saved }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + + + +- name: "restore state from backup (mangle, must NOT report a change)" + iptables_state: + path: "{{ iptables_saved }}" + table: mangle + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + +- name: "assert that results are as expected" + assert: + that: + - iptables_state is not changed + + + +- name: "create a blocking ruleset with a REJECT rule" + copy: + dest: "{{ iptables_tests }}" + content: | + *filter + -A INPUT -j REJECT + COMMIT + + + +- name: "fail to restore state from the test file (again)" + block: + - name: "restore state from the test file (bad rules, expected error -> rollback)" + iptables_state: + path: "{{ iptables_tests }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + + rescue: + - name: "explain expected failure" + assert: + that: + - iptables_state is not changed + - not iptables_state.applied + success_msg: >- + The previous error has been triggered to test the rollback. If you + are there, it means that 1) connection has been lost right after the + bad rules have been restored; 2) a rollback happened, so the bad + rules are not applied, finally; 3) module failed because it didn't + reach the wanted state, but at least host is not lost !!! + fail_msg: >- + The previous error has been triggered but its results are not as + expected. + +- name: "check that the expected failure happened" + assert: + that: + - iptables_state is failed + + + +- name: "fail to restore state from the test file (again)" + block: + - name: "try again, with a higher timeout (bad rules, same expected error)" + iptables_state: + path: "{{ iptables_tests }}" + state: restored + register: iptables_state + async: "{{ ansible_timeout }}" + poll: 0 + vars: + ansible_timeout: "{{ max_delay | d(300) }}" + + rescue: + - name: "explain expected failure" + assert: + that: + - iptables_state is not changed + - not iptables_state.applied + success_msg: >- + The previous error has been triggered to test the rollback. If you + are there, it means that 1) connection has been lost right after the + bad rules have been restored; 2) a rollback happened, so the bad + rules are not applied, finally; 3) module failed because it didn't + reach the wanted state, but at least host is not lost !!! + fail_msg: >- + The previous error has been triggered but its results are not as + expected. + +- name: "check that the expected failure happened" + assert: + that: + - iptables_state is failed diff --git a/ansible_collections/community/general/tests/integration/targets/ipwcli_dns/aliases b/ansible_collections/community/general/tests/integration/targets/ipwcli_dns/aliases new file mode 100644 index 000000000..b469f71b0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ipwcli_dns/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 + +# There is no Ericsson IPWorks +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/ipwcli_dns/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ipwcli_dns/tasks/main.yml new file mode 100644 index 000000000..9cbb4edc2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ipwcli_dns/tasks/main.yml @@ -0,0 +1,115 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Test code for ipwcli_dns +# 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: variables username, password, container, tld must be set + fail: + msg: 'Please set the variables: username, password, container and tld.' + when: username is not defined or password is not defined or container is not defined or tld is not defined + +- name: add a new A record + ipwcli_dns: + dnsname: example.{{ tld }} + type: A + container: '{{ container }}' + address: 127.0.0.1 + ttl: 100 + username: '{{ username }}' + password: '{{ password }}' + register: result + +- name: assert the new A record is added + assert: + that: + - result is not failed + - result is changed + - result.record == 'arecord example.{{ tld }} 127.0.0.1 -set ttl=100;container={{ container }}' + +- name: delete the A record + ipwcli_dns: + dnsname: example.{{ tld }} + type: A + container: '{{ container }}' + address: 127.0.0.1 + ttl: 100 + username: '{{ username }}' + password: '{{ password }}' + state: absent + register: result + +- name: assert the new A record is deleted + assert: + that: + - result is not failed + - result is changed + - result.record == 'arecord example.{{ tld }} 127.0.0.1 -set ttl=100;container={{ container }}' + +- name: delete not existing SRV record + ipwcli_dns: + dnsname: _sip._tcp.test.example.{{ tld }} + type: SRV + container: '{{ container }}' + target: example.{{ tld }} + port: 5060 + username: '{{ username }}' + password: '{{ password }}' + state: absent + register: result + +- name: assert the new a record + assert: + that: + - result is not failed + - result is not changed + - result.record == + 'srvrecord _sip._tcp.test.example.{{ tld }} -set ttl=3600;container={{ container }};priority=10;weight=10;port=5060;target=example.{{ tld }}' + +- name: add a SRV record with weight > 65535 against RFC 2782 + ipwcli_dns: + dnsname: _sip._tcp.test.example.{{ tld }} + type: SRV + container: '{{ container }}' + ttl: 100 + target: example.{{ tld }} + port: 5060 + weight: 65536 + username: '{{ username }}' + password: '{{ password }}' + register: result + ignore_errors: true + +- name: assert the failure of the new SRV record + assert: + that: + - result is failed + - result is not changed + - "'Out of UINT16 range' in result.stderr" + +- name: add NAPTR record (check_mode) + ipwcli_dns: + dnsname: test.example.{{ tld }} + type: NAPTR + preference: 10 + container: '{{ container }}' + ttl: 100 + order: 10 + service: 'SIP+D2T' + replacement: '_sip._tcp.test.example.{{ tld }}.' + flags: S + username: '{{ username }}' + password: '{{ password }}' + check_mode: true + register: result + +- name: assert the NAPTR check_mode + assert: + that: + - result is not failed + - result is changed diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/aliases b/ansible_collections/community/general/tests/integration/targets/iso_create/aliases new file mode 100644 index 000000000..4fb0bec81 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_create/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/1 +destructive +skip/aix +skip/python2.6 diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/files/test1.cfg b/ansible_collections/community/general/tests/integration/targets/iso_create/files/test1.cfg new file mode 100644 index 000000000..8cd712916 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_create/files/test1.cfg @@ -0,0 +1,61 @@ +#version=DEVEL + +# 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 + +# System authorization information +auth --enableshadow --passalgo=sha512 +# Use CDROM installation media +cdrom +# Use graphical install +graphical +# Run the Setup Agent on first boot +firstboot --enable +ignoredisk --only-use=sda +# Keyboard layouts +keyboard --vckeymap=us --xlayouts='us' +# System language +lang en_US.UTF-8 +# Network information +network --bootproto=dhcp --device=ens192 --ipv6=auto --no-activate +network --hostname=localhost.localdomain +# System services +services --enabled="chronyd" +# System timezone +timezone America/New_York --isUtc +# X Window System configuration information +xconfig --startxonboot +# System bootloader configuration +bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda +autopart --type=lvm +# Partition clearing information +clearpart --none --initlabel +#firewall --disable +services --disabled=firewalld +eula --agreed +# Reboot when the install is finished. +reboot + +%packages +@^graphical-server-environment +@base +@core +@desktop-debugging +@dial-up +@fonts +@gnome-desktop +@guest-agents +@guest-desktop-agents +@hardware-monitoring +@input-methods +@internet-browser +@multimedia +@print-client +@x11 +chrony +kexec-tools +open-vm-tools-desktop +%end +%addon com_redhat_kdump --enable --reserve-mb='auto' +%end diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/files/test_dir/test2.cfg b/ansible_collections/community/general/tests/integration/targets/iso_create/files/test_dir/test2.cfg new file mode 100644 index 000000000..8cd712916 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_create/files/test_dir/test2.cfg @@ -0,0 +1,61 @@ +#version=DEVEL + +# 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 + +# System authorization information +auth --enableshadow --passalgo=sha512 +# Use CDROM installation media +cdrom +# Use graphical install +graphical +# Run the Setup Agent on first boot +firstboot --enable +ignoredisk --only-use=sda +# Keyboard layouts +keyboard --vckeymap=us --xlayouts='us' +# System language +lang en_US.UTF-8 +# Network information +network --bootproto=dhcp --device=ens192 --ipv6=auto --no-activate +network --hostname=localhost.localdomain +# System services +services --enabled="chronyd" +# System timezone +timezone America/New_York --isUtc +# X Window System configuration information +xconfig --startxonboot +# System bootloader configuration +bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda +autopart --type=lvm +# Partition clearing information +clearpart --none --initlabel +#firewall --disable +services --disabled=firewalld +eula --agreed +# Reboot when the install is finished. +reboot + +%packages +@^graphical-server-environment +@base +@core +@desktop-debugging +@dial-up +@fonts +@gnome-desktop +@guest-agents +@guest-desktop-agents +@hardware-monitoring +@input-methods +@internet-browser +@multimedia +@print-client +@x11 +chrony +kexec-tools +open-vm-tools-desktop +%end +%addon com_redhat_kdump --enable --reserve-mb='auto' +%end diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/iso_create/meta/main.yml new file mode 100644 index 000000000..e7127a2d6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_create/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_remote_tmp_dir + - setup_remote_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/iso_create/tasks/main.yml new file mode 100644 index 000000000..d53217bd3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_create/tasks/main.yml @@ -0,0 +1,163 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Test code for iso_create module +# Copyright (c) 2020, Diane Wang (Tomorrow9) +# 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 pycdlib + pip: + name: pycdlib + extra_args: "-c {{ remote_constraints }}" + register: install_pycdlib +- debug: var=install_pycdlib + +- set_fact: + output_test_dir: '{{ remote_tmp_dir }}/test_iso_create' + +# - include_tasks: prepare_dest_dir.yml + +- name: Copy files and directories + copy: + src: '{{ item }}' + dest: '{{ remote_tmp_dir }}/{{ item }}' + loop: + - test1.cfg + - test_dir + +- name: Test check mode + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + dest_iso: "{{ output_test_dir }}/test.iso" + interchange_level: 3 + register: iso_result + check_mode: true +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test.iso" + register: iso_file +- debug: var=iso_file +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == False + +- name: Create iso file with a specified file + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + dest_iso: "{{ output_test_dir }}/test.iso" + interchange_level: 3 + register: iso_result +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test.iso" + register: iso_file + +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == True + +- name: Create iso file with a specified file and folder + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + - "{{ remote_tmp_dir }}/test_dir" + dest_iso: "{{ output_test_dir }}/test1.iso" + interchange_level: 3 + register: iso_result +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test1.iso" + register: iso_file + +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == True + +- name: Create iso file with volume identification string + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + dest_iso: "{{ output_test_dir }}/test2.iso" + vol_ident: "OEMDRV" + register: iso_result +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test2.iso" + register: iso_file + +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == True + +- name: Create iso file with Rock Ridge extension + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + dest_iso: "{{ output_test_dir }}/test3.iso" + rock_ridge: "1.09" + register: iso_result +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test3.iso" + register: iso_file + +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == True + +- name: Create iso file with Joliet extension + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + dest_iso: "{{ output_test_dir }}/test4.iso" + joliet: 3 + register: iso_result +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test4.iso" + register: iso_file + +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == True + +- name: Create iso file with UDF enabled + iso_create: + src_files: + - "{{ remote_tmp_dir }}/test1.cfg" + dest_iso: "{{ output_test_dir }}/test5.iso" + udf: true + register: iso_result +- debug: var=iso_result + +- name: Check if iso file created + stat: + path: "{{ output_test_dir }}/test5.iso" + register: iso_file + +- assert: + that: + - iso_result is changed + - iso_file.stat.exists == True diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml b/ansible_collections/community/general/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml new file mode 100644 index 000000000..d1f405b5f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml @@ -0,0 +1,13 @@ +# Test code for iso_create module +# Copyright (c) 2020, Diane Wang (Tomorrow9) +# 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 our testing sub-directory does not exist + file: + path: '{{ output_test_dir }}' + state: absent + +- name: Create our testing sub-directory + file: + path: '{{ output_test_dir }}' + state: directory diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases b/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases new file mode 100644 index 000000000..54a0f1a04 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases @@ -0,0 +1,13 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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 +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/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/meta/main.yml new file mode 100644 index 000000000..5b9177b12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/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_remote_tmp_dir + - setup_remote_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize.yml new file mode 100644 index 000000000..f7d7bffd1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize.yml @@ -0,0 +1,75 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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: Add a line to the file test02.cfg and make sure it succeed + ansible.builtin.lineinfile: + path: "{{ test_dir }}/test02.cfg" + regexp: "^test" + line: "test" + +- name: "Customize ISO file: add file, delete file and change file" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + delete_files: + - "/test01.cfg" + add_files: + - src_file: "{{ test_dir }}/test01.cfg" + dest_file: "/preseed/ubuntu.seed" + - src_file: "{{ test_dir }}/test02.cfg" + dest_file: "/test02.cfg" + +- include_tasks: iso_mount.yml + vars: + iso_name: "{{ dest_iso_name }}" + +- debug: var=mount_root_dir + +- name: Check the file test01.cfg is deleted + stat: + path: "{{ mount_root_dir }}/test01.cfg" + register: check_file + +- assert: + that: + - check_file.stat.exists == False + +- name: Check the file /preseed/ubuntu.seed is added + stat: + path: "{{ mount_root_dir }}/preseed/ubuntu.seed" + register: check_file + +- assert: + that: + - check_file.stat.exists == True + +- block: + - name: Get the content of file test02.cfg + command: "cat {{ mount_root_dir }}/test02.cfg" + register: get_file_content + + - set_fact: + file_contents: "{{ get_file_content.stdout }}" + when: ansible_distribution == 'RedHat' and ansible_distribution_version is version('7.9', '==') + +- name: Get the content of file test02.cfg + set_fact: + file_contents: "{{ lookup('file', mount_root_dir + '/test02.cfg') }}" + when: not (ansible_distribution == 'RedHat' and ansible_distribution_version is version('7.9', '==')) + +- fail: msg="Failed to replace the file test02.cfg" + when: file_contents != "test" + +- name: Umount ISO + mount: + path: "{{ mount_root_dir }}" + fstab: "{{ test_dir }}/temp.fstab" + state: unmounted + +- name: Delete line of file test02.cfg + ansible.builtin.lineinfile: + path: "{{ test_dir }}/test02.cfg" + regexp: "test" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_add_files.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_add_files.yml new file mode 100644 index 000000000..210767707 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_add_files.yml @@ -0,0 +1,34 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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: "Customize ISO file: add file" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test1.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + add_files: + - src_file: "{{ test_dir }}/test01.cfg" + dest_file: "preseed/ubuntu.seed" + + +- include_tasks: iso_mount.yml + vars: + iso_name: "{{ dest_iso_name }}" + +- debug: var=mount_root_dir + +- name: Check the file /preseed/ubuntu.seed is added + stat: + path: "{{ mount_root_dir }}/preseed/ubuntu.seed" + register: check_file + +- assert: + that: + - check_file.stat.exists == True + +- name: Umount ISO + mount: + path: "{{ mount_root_dir }}" + fstab: "{{ test_dir }}/temp.fstab" + state: unmounted diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_delete_files.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_delete_files.yml new file mode 100644 index 000000000..bceeeb53a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_delete_files.yml @@ -0,0 +1,34 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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: "Customize ISO file: delete file" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test1.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + delete_files: + - "test01.cfg" + +- debug: var=ansible_distribution + +- include_tasks: iso_mount.yml + vars: + iso_name: "{{ dest_iso_name }}" + +- debug: var=mount_root_dir + +- name: Check the file test01.cfg is deleted + stat: + path: "{{ mount_root_dir }}/test01.cfg" + register: check_file + +- assert: + that: + - check_file.stat.exists == False + +- name: Umount ISO + mount: + path: "{{ mount_root_dir }}" + fstab: "{{ test_dir }}/temp.fstab" + state: unmounted 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 new file mode 100644 index 000000000..b2130bb6b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml @@ -0,0 +1,71 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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: "Testcase: local resource ISO does not exists" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test11.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + register: customized_result + failed_when: customized_result.msg.find('does not exist') == -1 + +- name: "Testcase:: dest dir does not exists" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test1.iso" + dest_iso: "/aaa/{{ dest_iso_name }}" + register: customized_result + failed_when: customized_result.msg.find('does not exist') == -1 + +# Test: nothing is changed when no options "add files" and "delete files" +- block: + - name: "Testcase: no options 'add files' and 'delete files'" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test1.iso" + dest_iso: "{{ test_dir }}/iso_customize_nochanged.iso" + + - name: Get stats of a file test1.iso + ansible.builtin.stat: + path: "{{ test_dir }}/test1.iso" + register: iso_orginal + + - name: Get stats of a file iso_customize_nochanged.iso + ansible.builtin.stat: + path: "{{ test_dir }}/iso_customize_nochanged.iso" + register: iso_customized + + - name: compare size + fail: msg="Check we have nothing changed for customized ISO" + when: iso_orginal.stat.size != iso_customized.stat.size + +- name: "Testcase: delete the non-existing file in ISO" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test1.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + delete_files: + - "/test03.cfg" + register: customized_result + failed_when: customized_result.msg.find("does not exist") == -1 + +# Test: failed when local src file does not exists +- name: "Testcase: local src file does not exists" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + add_files: + - src_file: "{{ test_dir }}/test03.cfg" + dest_file: "/preseed/ubuntu.seed" + register: customized_result + 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 +- name: "Testcase: filenames with whitespaces" + community.general.iso_customize: + src_iso: "{{ test_dir }}/test.iso" + dest_iso: "{{ test_dir }}/{{ dest_iso_name }}" + add_files: + - src_file: " {{ test_dir }}/test01.cfg " + dest_file: "/preseed/ubuntu.seed" + register: customized_result + failed_when: customized_result.msg.find("does not exist") == -1 diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_mount.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_mount.yml new file mode 100644 index 000000000..cf4ab8199 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_mount.yml @@ -0,0 +1,39 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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: var=ansible_distribution + +- block: + - name: "Mount customized ISO on MAC" + command: "hdiutil attach {{ test_dir }}/{{ iso_name }} -mountroot {{ test_dir }}/iso_mount" + + # For MAC, we have different root directory for different type of ISO + - set_fact: + mount_root_dir: "{{ test_dir }}/iso_mount/disk_image" + + - set_fact: + mount_root_dir: "{{ test_dir }}/iso_mount/AUTOINSTALL" + when: iso_name.find('joliet') != -1 + + - set_fact: + mount_root_dir: "{{ test_dir }}/iso_mount/CDROM" + when: iso_name.find('udf') != -1 + when: ansible_distribution == "MacOSX" + +- block: + - name: "Mount {{ iso_name }} to {{ test_dir }}/iso_mount on localhost" + become: true + ansible.posix.mount: + path: "{{ test_dir }}/iso_mount" + src: "{{ test_dir }}/{{ iso_name }}" + opts: "ro,noauto" + fstab: "{{ test_dir }}/temp.fstab" + fstype: "iso9660" + state: mounted + + - set_fact: + mount_root_dir: "{{ test_dir }}/iso_mount" + when: + - ansible_distribution != "MacOSX" diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/main.yml new file mode 100644 index 000000000..dafd84dd5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/main.yml @@ -0,0 +1,94 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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: Skip some platforms which does not support ansible.posix.mount + meta: end_play + when: ansible_distribution in ['Alpine'] + +- set_fact: + test_dir: '{{ remote_tmp_dir }}/test_iso_customize' + +- include_tasks: prepare.yml + +- name: Create iso file with a specified file and directory + community.general.iso_create: + src_files: + - "{{ test_dir }}/test01.cfg" + - "{{ test_dir }}/test02.cfg" + dest_iso: "{{ test_dir }}/test.iso" + interchange_level: 3 + +- include_tasks: iso_customize.yml + vars: + dest_iso_name: "iso_customize.iso" + +- name: Create an ISO file with Rock Ridge extension + community.general.iso_create: + src_files: + - "{{ test_dir }}/test01.cfg" + - "{{ test_dir }}/test02.cfg" + dest_iso: "{{ test_dir }}/test.iso" + rock_ridge: "1.09" + +- include_tasks: iso_customize.yml + vars: + dest_iso_name: "iso_customize_rr.iso" + +- name: Create an ISO file with Joliet support + community.general.iso_create: + src_files: + - "{{ test_dir }}/test01.cfg" + - "{{ test_dir }}/test02.cfg" + dest_iso: "{{ test_dir }}/test.iso" + interchange_level: 3 + joliet: 3 + vol_ident: AUTOINSTALL + +- include_tasks: iso_customize.yml + vars: + dest_iso_name: "iso_customize_joliet.iso" + +- name: Create iso file with UDF enabled + community.general.iso_create: + src_files: + - "{{ test_dir }}/test01.cfg" + - "{{ test_dir }}/test02.cfg" + dest_iso: "{{ test_dir }}/test.iso" + udf: true + +- include_tasks: iso_customize.yml + vars: + dest_iso_name: "iso_customize_udf.iso" + +# Create initial iso for customzing with only option add_files/delete_files +- name: Create iso file with a specified file and directory + community.general.iso_create: + src_files: + - "{{ test_dir }}/test01.cfg" + dest_iso: "{{ test_dir }}/test1.iso" + interchange_level: 3 + +- include_tasks: iso_customize_add_files.yml + vars: + dest_iso_name: "iso_customize_add.iso" + +- include_tasks: iso_customize_delete_files.yml + vars: + dest_iso_name: "iso_customize_delete.iso" + +# Test: misc exception +- include_tasks: iso_customize_exception.yml + vars: + dest_iso_name: "iso_customize_exception.iso" + +- name: Delete testing sub-directory + ansible.builtin.file: + path: '{{ test_dir }}' + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/prepare.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/prepare.yml new file mode 100644 index 000000000..e3c860b7c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/prepare.yml @@ -0,0 +1,40 @@ +# Copyright (c) 2022, Ansible Project +# Copyright (c) 2022, VMware, Inc. All Rights Reserved. +# 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 pycdlib + ansible.builtin.pip: + name: pycdlib + extra_args: "-c {{ remote_constraints }}" + +- name: Make sure the previous testing sub-directory is deleted + ansible.builtin.file: + path: '{{ test_dir }}' + state: absent + +- name: Create our testing sub-directory + ansible.builtin.file: + path: '{{ test_dir }}' + state: directory + +- name: Create sub directory to mount customized ISO + ansible.builtin.file: + path: '{{ test_dir }}/iso_mount' + state: directory + +- name: Create temporary file test01.cfg for testing + ansible.builtin.file: + path: "{{ test_dir }}/test01.cfg" + state: touch + +- name: Add a line to the file test01.cfg and make sure it succeed + ansible.builtin.lineinfile: + path: "{{ test_dir }}/test01.cfg" + regexp: "^aaa" + line: "aaa" + +- name: Create temporary file test02.cfg for testing + ansible.builtin.file: + path: "{{ test_dir }}/test02.cfg" + state: touch diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/aliases b/ansible_collections/community/general/tests/integration/targets/iso_extract/aliases new file mode 100644 index 000000000..33041456a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/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/1 +needs/target/setup_epel +destructive +skip/aix +skip/osx # FIXME +skip/rhel9.0 # FIXME +skip/rhel9.1 # FIXME +skip/freebsd12.4 # FIXME +skip/freebsd13.2 # FIXME diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso b/ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso new file mode 100644 index 000000000..d06ff73ca Binary files /dev/null and b/ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso differ diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso.license b/ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/files/test.iso.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/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/iso_extract/tasks/7zip.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/7zip.yml new file mode 100644 index 000000000..e0f1586ce --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/7zip.yml @@ -0,0 +1,54 @@ +--- +# Test code for the iso_extract module. +# Copyright (c) 2017, James Tanner +# Copyright (c) 2017, Dag Wieers +# 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: Gather facts + setup: + become: true + +- name: Include distribution specific variables + include_vars: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - "{{ ansible_facts.distribution }}.yml" + - "{{ ansible_facts.os_family }}.yml" + - default.yml + paths: + - "{{ role_path }}/vars" + +- name: "{{ ansible_facts.os_family | upper }} | Install 7zip package" + action: "{{ ansible_facts.pkg_mgr }}" + args: + name: "{{ iso_extract_7zip_package }}" + state: present + when: ansible_facts.distribution != 'MacOSX' + +- name: macOS + when: ansible_facts.distribution == 'MacOSX' + block: + - name: MACOS | Find brew binary + command: which brew + register: brew_which + when: ansible_distribution in ['MacOSX'] + + - name: MACOS | Get owner of brew binary + stat: + path: "{{ brew_which.stdout }}" + register: brew_stat + when: ansible_distribution in ['MacOSX'] + + - name: MACOS | Install 7zip package + homebrew: + name: p7zip + state: present + update_homebrew: false + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + # Newer versions of brew want to compile a package which takes a long time. Do not upgrade homebrew until a + # proper solution can be found + environment: + HOMEBREW_NO_AUTO_UPDATE: "True" diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/main.yml new file mode 100644 index 000000000..67ebfa7ab --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/main.yml @@ -0,0 +1,43 @@ +--- +#################################################################### +# 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 iso_extract module. +# Copyright (c) 2017, James Tanner +# Copyright (c) 2017, Dag Wieers +# 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_fact: + output_test_dir: '{{ remote_tmp_dir }}/test_iso_extract' + +- name: Install EPEL repository (RHEL only) + include_role: + name: setup_epel + when: + - ansible_distribution in ['RedHat', 'CentOS'] + - ansible_distribution_major_version is version('9', '<') + +- name: Install 7zip + import_tasks: 7zip.yml + +- name: Prepare environment + import_tasks: prepare.yml + +- name: Test in normal mode + import_tasks: tests.yml + vars: + in_check_mode: false + +- name: Prepare environment + import_tasks: prepare.yml + +- name: Test in check-mode + import_tasks: tests.yml + vars: + in_check_mode: true + check_mode: true + +# FIXME - fill this in after figuring out how to allow mounts diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/prepare.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/prepare.yml new file mode 100644 index 000000000..57e10c0db --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/prepare.yml @@ -0,0 +1,21 @@ +--- +# Test code for the iso_extract module. +# Copyright (c) 2017, James Tanner +# Copyright (c) 2017, Dag Wieers +# 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 our testing sub-directory does not exist + file: + path: '{{ output_test_dir }}' + state: absent + +- name: Create our testing sub-directory + file: + path: '{{ output_test_dir }}' + state: directory + +- name: copy the iso to the test dir + copy: + src: test.iso + dest: '{{ output_test_dir }}' diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/tests.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/tests.yml new file mode 100644 index 000000000..6919a7c2e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/tasks/tests.yml @@ -0,0 +1,40 @@ +--- +# Test code for the iso_extract module. +# Copyright (c) 2017, James Tanner +# Copyright (c) 2017, Dag Wieers +# 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: Extract the iso + iso_extract: + image: '{{ output_test_dir }}/test.iso' + dest: '{{ output_test_dir }}' + files: + - 1.txt + - 2.txt + register: iso_extract_test0 + +- assert: + that: + - iso_extract_test0 is changed + +- name: Extract the iso again + iso_extract: + image: '{{ output_test_dir }}/test.iso' + dest: '{{ output_test_dir }}' + files: + - 1.txt + - 2.txt + register: iso_extract_test0_again + +- name: Test iso_extract_test0_again (normal mode) + assert: + that: + - iso_extract_test0_again is not changed + when: not in_check_mode + +- name: Test iso_extract_test0_again (check-mode) + assert: + that: + - iso_extract_test0_again is changed + when: in_check_mode diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Alpine.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Alpine.yml new file mode 100644 index 000000000..bcb92f79b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Alpine.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 + +iso_extract_7zip_package: p7zip diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Archlinux.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Archlinux.yml new file mode 100644 index 000000000..bcb92f79b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Archlinux.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 + +iso_extract_7zip_package: p7zip diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Debian.yml new file mode 100644 index 000000000..09436ceb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Debian.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 + +iso_extract_7zip_package: p7zip-full diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/FreeBSD.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/FreeBSD.yml new file mode 100644 index 000000000..bcb92f79b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/FreeBSD.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 + +iso_extract_7zip_package: p7zip diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/RedHat.yml new file mode 100644 index 000000000..2bcdf4bff --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/RedHat.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 + +iso_extract_7zip_package: p7zip-plugins diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Suse.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Suse.yml new file mode 100644 index 000000000..1b695ce72 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Suse.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 + +# The 7z executable moved from p7zip to p7zip-full; +# see https://build.opensuse.org/package/view_file/openSUSE:Leap:15.2/p7zip/p7zip.changes?expand=1 +iso_extract_7zip_package: p7zip-full diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Ubuntu.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Ubuntu.yml new file mode 100644 index 000000000..09436ceb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/Ubuntu.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 + +iso_extract_7zip_package: p7zip-full diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/default.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/vars/default.yml @@ -0,0 +1,4 @@ +--- +# 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/java_cert/aliases b/ansible_collections/community/general/tests/integration/targets/java_cert/aliases new file mode 100644 index 000000000..573cb189b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/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 +destructive +skip/aix +skip/osx +skip/macos +skip/freebsd +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/java_cert/defaults/main.yml new file mode 100644 index 000000000..ebac2789b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/defaults/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 + +test_pkcs12_path: testpkcs.p12 +test_keystore_path: keystore.jks +test_keystore2_path: "{{ remote_tmp_dir }}/keystore2.jks" +test_keystore2_password: changeit +test_cert_path: "{{ remote_tmp_dir }}/cert.pem" +test_key_path: "{{ remote_tmp_dir }}/key.pem" +test_csr_path: "{{ remote_tmp_dir }}/req.csr" +test_cert2_path: "{{ remote_tmp_dir }}/cert2.pem" +test_key2_path: "{{ remote_tmp_dir }}/key2.pem" +test_csr2_path: "{{ remote_tmp_dir }}/req2.csr" +test_pkcs_path: "{{ remote_tmp_dir }}/cert.p12" +test_pkcs2_path: "{{ remote_tmp_dir }}/cert2.p12" +test_ssl: setupSSLServer.py +test_ssl_port: 21500 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 new file mode 100644 index 000000000..4b0a42185 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import ssl +import os +import sys + +root_dir = sys.argv[1] +port = int(sys.argv[2]) + +try: + from BaseHTTPServer import HTTPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler +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')) +httpd.handle_request() diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12 b/ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12 new file mode 100644 index 000000000..e0fee618c Binary files /dev/null and b/ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12 differ diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12.license b/ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/files/testpkcs.p12.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/java_cert/meta/main.yml new file mode 100644 index 000000000..0371df88e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/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_java_keytool + - setup_openssl + - setup_remote_tmp_dir diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/main.yml new file mode 100644 index 000000000..25ec87e8f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/main.yml @@ -0,0 +1,116 @@ +--- +#################################################################### +# 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 + +- when: has_java_keytool + block: + + - name: prep pkcs12 file + ansible.builtin.copy: + src: "{{ test_pkcs12_path }}" + dest: "{{ remote_tmp_dir }}/{{ test_pkcs12_path }}" + + - name: import pkcs12 + community.general.java_cert: + pkcs12_path: "{{ remote_tmp_dir }}/{{ test_pkcs12_path }}" + pkcs12_password: changeit + pkcs12_alias: default + cert_alias: default + keystore_path: "{{ remote_tmp_dir }}/{{ test_keystore_path }}" + keystore_pass: changeme_keystore + keystore_create: true + state: present + register: result_success + + - name: verify success + ansible.builtin.assert: + that: + - result_success is successful + + - name: import pkcs12 with wrong password + community.general.java_cert: + pkcs12_path: "{{ remote_tmp_dir }}/{{ test_pkcs12_path }}" + pkcs12_password: wrong_pass + pkcs12_alias: default + cert_alias: default_new + keystore_path: "{{ remote_tmp_dir }}/{{ test_keystore_path }}" + keystore_pass: changeme_keystore + keystore_create: true + state: present + ignore_errors: true + register: result_wrong_pass + + - name: verify fail with wrong import password + ansible.builtin.assert: + that: + - result_wrong_pass is failed + + - name: test fail on mutually exclusive params + community.general.java_cert: + cert_path: ca.crt + pkcs12_path: "{{ remote_tmp_dir }}/{{ test_pkcs12_path }}" + cert_alias: default + keystore_path: "{{ remote_tmp_dir }}/{{ test_keystore_path }}" + keystore_pass: changeme_keystore + keystore_create: true + state: present + ignore_errors: true + register: result_excl_params + + - name: verify failed exclusive params + ansible.builtin.assert: + that: + - result_excl_params is failed + + - name: test fail on missing required params + community.general.java_cert: + keystore_path: "{{ remote_tmp_dir }}/{{ test_keystore_path }}" + keystore_pass: changeme_keystore + state: absent + ignore_errors: true + register: result_missing_required_param + + - name: verify failed missing required params + ansible.builtin.assert: + that: + - result_missing_required_param is failed + + - name: delete object based on cert_alias parameter + community.general.java_cert: + keystore_path: "{{ remote_tmp_dir }}/{{ test_keystore_path }}" + keystore_pass: changeme_keystore + cert_alias: default + state: absent + ignore_errors: true + register: result_alias_deleted + + - name: verify object successfully deleted + ansible.builtin.assert: + that: + - result_alias_deleted is successful + + - name: include extended test suite + import_tasks: state_change.yml + + - name: cleanup environment + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ remote_tmp_dir }}/{{ test_pkcs12_path }}" + - "{{ remote_tmp_dir }}/{{ test_keystore_path }}" + - "{{ test_keystore2_path }}" + - "{{ test_cert_path }}" + - "{{ test_key_path }}" + - "{{ test_csr_path }}" + - "{{ test_cert2_path }}" + - "{{ test_key2_path }}" + - "{{ test_csr2_path }}" + - "{{ test_pkcs_path }}" + - "{{ test_pkcs2_path }}" 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 new file mode 100644 index 000000000..e135a60a3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml @@ -0,0 +1,298 @@ +--- +# 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 + +# +# Prepare X509 and PKCS#12 materials +# + +- name: Create private keys + community.crypto.openssl_privatekey: + path: "{{ item }}" + mode: "u=rw,go=" + loop: + - "{{ test_key_path }}" + - "{{ test_key2_path }}" + +- name: Generate CSR for self-signed certificate used as a placeholder to create the java keystore + community.crypto.openssl_csr: + path: "{{ test_csr_path }}" + privatekey_path: "{{ test_key_path }}" + commonName: "localhost" + +- name: Generate CSR for self-signed certificate used for testing + community.crypto.openssl_csr: + path: "{{ test_csr2_path }}" + privatekey_path: "{{ test_key2_path }}" + commonName: "localhost" + +- name: Generate the self-signed cert used as a placeholder to create the java keystore + community.crypto.x509_certificate: + path: "{{ test_cert_path }}" + csr_path: "{{ test_csr_path }}" + privatekey_path: "{{ test_key_path }}" + provider: selfsigned + +- name: Generate the self signed cert we will use for testing + community.crypto.x509_certificate: + path: "{{ test_cert2_path }}" + csr_path: "{{ test_csr2_path }}" + privatekey_path: "{{ test_key2_path }}" + provider: selfsigned + +- name: Create the pkcs12 archive from the test x509 cert + community.crypto.openssl_pkcs12: + name: "test_pkcs12_cert" + path: "{{ test_pkcs_path }}" + passphrase: "{{ test_keystore2_password }}" + certificate_path: "{{ test_cert_path }}" + privatekey_path: "{{ test_key_path }}" + when: + - "not (ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<'))" + +- name: Create the pkcs12 archive from the test x509 cert (command) + ansible.builtin.command: + cmd: > + openssl pkcs12 -export + -in {{ test_cert_path }} + -inkey {{ test_key_path }} + -name test_pkcs12_cert + -out {{ test_pkcs_path }} + -passout stdin + stdin: "{{ test_keystore2_password }}" + when: + - "ansible_os_family == 'RedHat'" + - "ansible_distribution_version is version('8.0', '<')" + +- name: Create the pkcs12 archive from the certificate we will be trying to add to the keystore + community.crypto.openssl_pkcs12: + name: "test_pkcs12_cert" + path: "{{ test_pkcs2_path }}" + passphrase: "{{ test_keystore2_password }}" + certificate_path: "{{ test_cert2_path }}" + privatekey_path: "{{ test_key2_path }}" + when: + - "not (ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<'))" + +- name: Create the pkcs12 archive from the certificate we will be trying to add to the keystore (command) + ansible.builtin.command: + cmd: > + openssl pkcs12 -export + -in {{ test_cert2_path }} + -inkey {{ test_key2_path }} + -name test_pkcs12_cert + -out {{ test_pkcs2_path }} + -passout stdin + stdin: "{{ test_keystore2_password }}" + when: + - "ansible_os_family == 'RedHat'" + - "ansible_distribution_version is version('8.0', '<')" + +# +# Run tests +# + +- name: try to create the test keystore based on the just created pkcs12, keystore_create flag not enabled + community.general.java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + ignore_errors: true + register: result_x509_changed + +- name: Verify the x509 status is failed + ansible.builtin.assert: + that: + - result_x509_changed is failed + +- name: Create the test keystore based on the just created pkcs12 + community.general.java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + keystore_create: true + +- name: List newly created keystore content + ansible.builtin.command: + cmd: "keytool -list -keystore {{ test_keystore2_path }}" + stdin: "{{ test_keystore2_password }}" + register: keytool_list_keystore + +- name: Assert that the keystore has a private key entry + ansible.builtin.assert: + that: + - "keytool_list_keystore.stdout_lines[5] is match('test_pkcs12_cert,.*, PrivateKeyEntry, $')" + +- name: try to import from pkcs12 a non existing alias + community.general.java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: non_existing_alias + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + keystore_create: true + ignore_errors: true + register: result_x509_changed + +- name: Verify the x509 status is failed + ansible.builtin.assert: + that: + - result_x509_changed is failed + +- name: import initial test certificate from file path + community.general.java_cert: + cert_alias: test_cert + cert_path: "{{ test_cert_path }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + keystore_create: true + state: present + register: result_x509_changed + +- name: Verify the x509 status is changed + ansible.builtin.assert: + that: + - result_x509_changed is changed + +- name: | + Import the newly created certificate. This is our main test. + If the java_cert has been updated properly, then this task will report changed each time + since the module will be comparing the hash of the certificate instead of validating that the alias + simply exists + community.general.java_cert: + cert_alias: test_cert + cert_path: "{{ test_cert2_path }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + register: result_x509_changed + +- name: Verify the x509 status is changed + ansible.builtin.assert: + that: + - result_x509_changed is changed + +- name: | + We also want to make sure that the status doesnt change if we import the same cert + community.general.java_cert: + cert_alias: test_cert + cert_path: "{{ test_cert2_path }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + register: result_x509_succeeded + +- name: Verify the x509 status is ok + ansible.builtin.assert: + that: + - result_x509_succeeded is succeeded + +- name: > + Ensure the original pkcs12 cert is in the keystore + community.general.java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + +- name: | + Perform the same test, but we will now be testing the pkcs12 functionality + If we add a different pkcs12 cert with the same alias, we should have a changed result, NOT the same + community.general.java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs2_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + register: result_pkcs12_changed + +- name: Verify the pkcs12 status is changed + ansible.builtin.assert: + that: + - result_pkcs12_changed is changed + +- name: | + We are requesting the same cert now, so the status should show OK + community.general.java_cert: + cert_alias: test_pkcs12_cert + pkcs12_alias: test_pkcs12_cert + pkcs12_path: "{{ test_pkcs2_path }}" + pkcs12_password: "{{ test_keystore2_password }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + register: result_pkcs12_succeeded + +- name: Verify the pkcs12 status is ok + ansible.builtin.assert: + that: + - result_pkcs12_succeeded is succeeded + +- name: Copy the ssl server script + copy: + src: "setupSSLServer.py" + dest: "{{ remote_tmp_dir }}" + +- name: Create an SSL server that we will use for testing URL imports + command: "{{ ansible_python.executable }} {{ remote_tmp_dir }}/setupSSLServer.py {{ remote_tmp_dir }} {{ test_ssl_port }}" + async: 10 + poll: 0 + +- name: "Wait for one second to make sure that the serve script has actually been started" + pause: + seconds: 1 + +- name: | + Download the original cert.pem from our temporary server. The current cert should contain + cert2.pem. Importing this cert should return a status of changed + community.general.java_cert: + cert_alias: test_cert_localhost + cert_url: localhost + cert_port: "{{ test_ssl_port }}" + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: present + register: result_url_changed + +- name: Verify that the url status is changed + ansible.builtin.assert: + that: + - result_url_changed is changed + +- name: Ensure we can remove the x509 cert + community.general.java_cert: + cert_alias: test_cert + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: absent + register: result_x509_absent + +- name: Verify the x509 cert is absent + ansible.builtin.assert: + that: + - result_x509_absent is changed + +- name: Ensure we can remove the certificate imported from pkcs12 archive + community.general.java_cert: + cert_alias: test_pkcs12_cert + keystore_path: "{{ test_keystore2_path }}" + keystore_pass: "{{ test_keystore2_password }}" + state: absent + register: result_pkcs12_absent + +- name: Verify the pkcs12 archive is absent + ansible.builtin.assert: + that: + - result_pkcs12_absent is changed diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/aliases b/ansible_collections/community/general/tests/integration/targets/java_keystore/aliases new file mode 100644 index 000000000..573cb189b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/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 +destructive +skip/aix +skip/osx +skip/macos +skip/freebsd +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/java_keystore/defaults/main.yml new file mode 100644 index 000000000..b51461462 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/defaults/main.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 + +java_keystore_certs: + - name: cert + commonName: example.com + - name: cert-pw + passphrase: hunter2 + commonName: example.com + +java_keystore_new_certs: + - name: cert2 + keyname: cert + commonName: example.org + - name: cert2-pw + keyname: cert-pw + passphrase: hunter2 + commonName: example.org diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/java_keystore/meta/main.yml new file mode 100644 index 000000000..0371df88e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/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_java_keytool + - setup_openssl + - setup_remote_tmp_dir diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/main.yml new file mode 100644 index 000000000..2a95cfe50 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/main.yml @@ -0,0 +1,43 @@ +--- +#################################################################### +# 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 + +- when: has_java_keytool + connection: local + block: + - name: Include tasks to create ssl materials on the controller + include_tasks: prepare.yml + +- set_fact: + ssl_backends: ['openssl'] + +- set_fact: + ssl_backends: "{{ ssl_backends + ['cryptography'] }}" + when: cryptography_version.stdout is version('3.0', '>=') + +- when: has_java_keytool + block: + - name: Include tasks to play with 'certificate' and 'private_key' contents + include_tasks: tests.yml + vars: + remote_cert: false + loop: "{{ ssl_backends }}" + loop_control: + loop_var: ssl_backend + + - name: Include tasks to create ssl materials on the remote host + include_tasks: prepare.yml + + - name: Include tasks to play with 'certificate_path' and 'private_key_path' locations + include_tasks: tests.yml + vars: + remote_cert: true + loop: "{{ ssl_backends }}" + loop_control: + loop_var: ssl_backend diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/prepare.yml b/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/prepare.yml new file mode 100644 index 000000000..7c4c5c98d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/prepare.yml @@ -0,0 +1,37 @@ +--- +# 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 test directory + ansible.builtin.file: + path: "{{ remote_tmp_dir }}" + state: directory + +- name: Create private keys + community.crypto.openssl_privatekey: + path: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | default(item.name)) ~ '.key' }}" + size: 2048 # this should work everywhere + # The following is more efficient, but might not work everywhere: + # type: ECC + # curve: secp384r1 + cipher: "{{ 'auto' if item.passphrase is defined else omit }}" + passphrase: "{{ item.passphrase | default(omit) }}" + loop: "{{ java_keystore_certs }}" + +- name: Create CSRs + community.crypto.openssl_csr: + path: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.csr' }}" + privatekey_path: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | default(item.name)) ~ '.key' }}" + privatekey_passphrase: "{{ item.passphrase | default(omit) }}" + commonName: "{{ item.commonName }}" + loop: "{{ java_keystore_certs + java_keystore_new_certs }}" + +- name: Create certificates + community.crypto.x509_certificate: + path: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.pem' }}" + csr_path: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.csr' }}" + privatekey_path: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | default(item.name)) ~ '.key' }}" + privatekey_passphrase: "{{ item.passphrase | default(omit) }}" + provider: selfsigned + loop: "{{ java_keystore_certs + java_keystore_new_certs }}" diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/tests.yml b/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/tests.yml new file mode 100644 index 000000000..899fe27e8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/tasks/tests.yml @@ -0,0 +1,313 @@ +--- +# 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 test directory + ansible.builtin.file: + path: "{{ remote_tmp_dir }}" + state: directory + +- name: Ensure the Java keystore does not exist (cleanup between tests) + ansible.builtin.file: + path: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.jks' }}" + state: absent + loop: "{{ java_keystore_certs }}" + loop_control: + label: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.jks' }}" + + +- name: Read certificates + slurp: + src: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.pem' }}" + loop: "{{ java_keystore_certs }}" + when: not remote_cert + register: certificates + +- name: Read certificate keys + slurp: + src: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.key' }}" + loop: "{{ java_keystore_certs }}" + when: not remote_cert + register: certificate_keys + +- name: Create a Java keystore for the given ({{ 'remote' if remote_cert else 'local' }}) certificates (check mode) + community.general.java_keystore: &java_keystore_params + name: example + dest: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.jks' }}" + certificate: "{{ omit if remote_cert else (certificates.results[loop_index].content | b64decode) }}" + private_key: "{{ omit if remote_cert else (certificate_keys.results[loop_index].content | b64decode) }}" + certificate_path: "{{ omit if not remote_cert else remote_tmp_dir ~ '/' ~ item.name ~ '.pem' }}" + private_key_path: "{{ omit if not remote_cert else remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.key' }}" + private_key_passphrase: "{{ item.passphrase | d(omit) }}" + password: changeit + ssl_backend: "{{ ssl_backend }}" + keystore_type: "{{ item.keystore_type | d(omit) }}" + loop: "{{ java_keystore_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_check + +- name: Create a Java keystore for the given certificates + community.general.java_keystore: *java_keystore_params + loop: "{{ java_keystore_certs }}" + loop_control: + index_var: loop_index + register: result + + +- name: Create a Java keystore for the given certificates (idempotency, check mode) + community.general.java_keystore: *java_keystore_params + loop: "{{ java_keystore_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_idem_check + +- name: Create a Java keystore for the given certificates (idempotency) + community.general.java_keystore: *java_keystore_params + loop: "{{ java_keystore_certs }}" + loop_control: + index_var: loop_index + register: result_idem + + +- name: Read certificates (new) + slurp: + src: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.pem' }}" + loop: "{{ java_keystore_new_certs }}" + when: not remote_cert + register: certificates_new + +- name: Read certificate keys (new) + slurp: + src: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.key' }}" + loop: "{{ java_keystore_new_certs }}" + when: not remote_cert + register: certificate_keys_new + +- name: Create a Java keystore for the given certificates (certificate changed, check mode) + community.general.java_keystore: &java_keystore_params_new_certs + name: example + dest: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.jks' }}" + certificate: "{{ omit if remote_cert else (certificates_new.results[loop_index].content | b64decode) }}" + private_key: "{{ omit if remote_cert else (certificate_keys_new.results[loop_index].content | b64decode) }}" + certificate_path: "{{ omit if not remote_cert else remote_tmp_dir ~ '/' ~ item.name ~ '.pem' }}" + private_key_path: "{{ omit if not remote_cert else remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.key' }}" + private_key_passphrase: "{{ item.passphrase | d(omit) }}" + password: changeit + ssl_backend: "{{ ssl_backend }}" + keystore_type: "{{ item.keystore_type | d(omit) }}" + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_change_check + +- name: Create a Java keystore for the given certificates (certificate changed) + community.general.java_keystore: *java_keystore_params_new_certs + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_change + + +- name: Create a Java keystore for the given certificates (alias changed, check mode) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_alias_change_check + +- name: Create a Java keystore for the given certificates (alias changed) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_alias_change + + +- name: Create a Java keystore for the given certificates (password changed, check mode) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_pw_change_check + +- name: Create a Java keystore for the given certificates (password changed) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_pw_change + + +- name: Create a Java keystore for the given certificates (force keystore type pkcs12, check mode) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + keystore_type: pkcs12 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_type_pkcs12_check + +- name: Create a Java keystore for the given certificates (force keystore type jks, check mode) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + keystore_type: jks + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_type_jks_check + +- name: Create a Java keystore for the given certificates (force keystore type jks) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + keystore_type: jks + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_type_jks + + +- name: Stat keystore (before failure) + ansible.builtin.stat: + path: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.jks' }}" + loop: "{{ java_keystore_new_certs }}" + register: result_stat_before + +- name: Fail to create a Java keystore for the given certificates (password too short) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: short + keystore_type: jks + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_fail_jks + ignore_errors: true + +- name: Stat keystore (after failure) + ansible.builtin.stat: + path: "{{ remote_tmp_dir ~ '/' ~ (item.keyname | d(item.name)) ~ '.jks' }}" + loop: "{{ java_keystore_new_certs }}" + register: result_stat_after + + +- name: Create a Java keystore for the given certificates (keystore type changed, check mode) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + keystore_type: pkcs12 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_type_change_check + +- name: Create a Java keystore for the given certificates (keystore type changed) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + keystore_type: pkcs12 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_type_change + + +- name: Create a Java keystore for the given certificates (omit keystore type, check mode) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + check_mode: true + register: result_type_omit_check + +- name: Create a Java keystore for the given certificates (omit keystore type) + community.general.java_keystore: + <<: *java_keystore_params_new_certs + name: foobar + password: hunter2 + loop: "{{ java_keystore_new_certs }}" + loop_control: + index_var: loop_index + register: result_type_omit + + +- name: Check that the remote certificates have not been removed + ansible.builtin.file: + path: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.pem' }}" + state: file + loop: "{{ java_keystore_certs + java_keystore_new_certs }}" + when: remote_cert + +- name: Check that the remote private keys have not been removed + ansible.builtin.file: + path: "{{ remote_tmp_dir ~ '/' ~ item.name ~ '.key' }}" + state: file + loop: "{{ java_keystore_certs }}" + when: remote_cert + +- name: Validate results + assert: + that: + - result is changed + - result_check is changed + - result_idem is not changed + - result_idem_check is not changed + - result_change is changed + - result_change_check is changed + - result_alias_change is changed + - result_alias_change_check is changed + - result_pw_change is changed + - result_pw_change_check is changed + + # We don't know if we start from jks or pkcs12 format, anyway check mode + # and actual mode must return the same 'changed' state, and 'jks' and + # 'pkcs12' must give opposite results on a same host. + - result_type_jks_check.changed != result_type_pkcs12_check.changed + - result_type_jks_check.changed == result_type_jks.changed + + - result_type_change is changed + - result_type_change_check is changed + - result_type_omit is not changed + - result_type_omit_check is not changed + + # keystore properties must remain the same after failure + - result_fail_jks is failed + - result_stat_before.results[0].stat.uid == result_stat_after.results[0].stat.uid + - result_stat_before.results[1].stat.uid == result_stat_after.results[1].stat.uid + - result_stat_before.results[0].stat.gid == result_stat_after.results[0].stat.gid + - result_stat_before.results[1].stat.gid == result_stat_after.results[1].stat.gid + - result_stat_before.results[0].stat.mode == result_stat_after.results[0].stat.mode + - result_stat_before.results[1].stat.mode == result_stat_after.results[1].stat.mode + - result_stat_before.results[0].stat.checksum == result_stat_after.results[0].stat.checksum + - result_stat_before.results[1].stat.checksum == result_stat_after.results[1].stat.checksum diff --git a/ansible_collections/community/general/tests/integration/targets/jboss/aliases b/ansible_collections/community/general/tests/integration/targets/jboss/aliases new file mode 100644 index 000000000..38b6706fe --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jboss/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 +destructive +skip/aix +skip/osx +skip/macos +skip/freebsd +skip/rhel +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/jboss/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/jboss/meta/main.yml new file mode 100644 index 000000000..c2bf37df7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jboss/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_wildfly_server diff --git a/ansible_collections/community/general/tests/integration/targets/jboss/tasks/jboss.yml b/ansible_collections/community/general/tests/integration/targets/jboss/tasks/jboss.yml new file mode 100644 index 000000000..2403a02c4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jboss/tasks/jboss.yml @@ -0,0 +1,238 @@ +--- +# Copyright (c) 2019, Andrew Klychkov (@Andersson007) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# Integration tests for jboss module. +# SPDX-License-Identifier: GPL-3.0-or-later + +# helloworld.war (got from https://github.com/aeimer/java-example-helloworld-war/) is licensed +# under the MIT license: +# +# Copyright (c) 2017 Alex Eimer +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# =============================== +# Module's note section contains: +# "- The JBoss standalone deployment-scanner has to be enabled in standalone.xml" +# +# Also from https://docs.jboss.org/author/display/WFLY10/Application+deployment?_sscc=t +# "Deployment content (for example, war, ear, jar, and sar files) can be placed +# in the standalone/deployments directory of the WildFly distribution, +# in order to be automatically deployed into the server runtime. +# For this to work the deployment-scanner subsystem must be present. +# The scanner periodically checks the contents of the deployments directory +# and reacts to changes by updating the server." +# Regarding the information above JBoss server must be installed and running for full test suite. +# We use WildFly server, free alternative, instead. See setup_wildfly_server role for more information. + +- vars: + war_file_1: 'helloworld-1.war' + war_file_1_path: '{{ wf_homedir }}/{{ war_file_1 }}' + fake_src_path: /fake/src + test_deployment: helloworld-1.war + task_parameters: &task_parameters + become_user: '{{ wf_user }}' + become: true + register: result + + block: + - name: Create test files + <<: *task_parameters + get_url: + url: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/jboss/{{ war_file_1 }}' + dest: '{{ wf_homedir }}' + + ################## + # Start the tests: + + # Test if state=present and not deployed, check_mode: + - name: jboss - deploy war in check_mode, the default deploy_path + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + src: '{{ war_file_1_path }}' + check_mode: true + + - assert: + that: + - result is changed + + # Check + - name: check that nothing changed after the previous step + <<: *task_parameters + file: + path: '{{ deploy_dir }}/{{ war_file_1 }}.deployed' + ignore_errors: true + + - assert: + that: + - "'is absent' in result.msg" + + # Test if state=present and not deployed, actual mode: + - name: jboss - deploy war + <<: *task_parameters + jboss: + deployment: helloworld-1.war + deploy_path: '{{ deploy_dir }}' + src: '{{ war_file_1_path }}' + + - assert: + that: + - result is changed + + # Check + - name: check that the file is deployed after the previous step + <<: *task_parameters + file: + path: '{{ deploy_dir }}/{{ war_file_1 }}.deployed' + + - assert: + that: + - result.state == 'file' + + # Test if state=present and deployed in check mode, try again: + - name: jboss - try again to deploy war in check_mode, war is deployed now + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + src: '{{ war_file_1_path }}' + deploy_path: '{{ deploy_dir }}' + check_mode: true + + - assert: + that: + - result is not changed + + # Test if state=present and deployed, try again: + - name: jboss - try again to deploy war in actual mode, war is deployed now + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + src: '{{ war_file_1_path }}' + deploy_path: '{{ deploy_dir }}' + + - assert: + that: + - result is not changed + + # Check + - name: check that nothing changed after the previous step + <<: *task_parameters + file: + path: '{{ deploy_dir }}/{{ war_file_1 }}.deployed' + + - assert: + that: + - result.state == 'file' + + # Test if state=absent and deployed: + - name: jboss - undeploy war in check_mode, war is deployed + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + deploy_path: '{{ deploy_dir }}' + state: absent + check_mode: true + + - assert: + that: + - result is changed + + - name: check that nothing actually changed after the previous step + <<: *task_parameters + file: + path: '{{ deploy_dir }}/{{ war_file_1 }}.deployed' + + - assert: + that: + - result.state == 'file' + + # Test if state=absent and deployed: + - name: jboss - undeploy war in actual mode, war is deployed + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + deploy_path: '{{ deploy_dir }}' + state: absent + + - assert: + that: + - result is changed + + - name: check that file is undeployed after the previous step + <<: *task_parameters + file: + path: '{{ deploy_dir }}/{{ war_file_1 }}.undeployed' + + - assert: + that: + - result.state == 'file' + + # Test if state=absent and undeployed: + - name: jboss - undeploy war in check_mode, war is undeployed + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + deploy_path: '{{ deploy_dir }}' + state: absent + check_mode: true + + - assert: + that: + - result is not changed + + # Test if state=absent and undeployed: + - name: jboss - undeploy war in actual_mode, war is undeployed + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + deploy_path: '{{ deploy_dir }}' + state: absent + + - assert: + that: + - result is not changed + + # Test fake src: + - name: jboss - test fake src + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + deploy_path: '{{ deploy_dir }}' + src: '{{ fake_src_path }}' + state: present + ignore_errors: true + + - assert: + that: + - result is failed + - "'Source file {{ fake_src_path }} does not exist.' in result.msg" + + # Test errors where state=present and src is not passed: + - name: jboss - must fail when state=present and src is not passed + <<: *task_parameters + jboss: + deployment: '{{ war_file_1 }}' + state: present + ignore_errors: true + + - assert: + that: + - result is failed + - "'state is present but all of the following are missing: src' in result.msg" diff --git a/ansible_collections/community/general/tests/integration/targets/jboss/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/jboss/tasks/main.yml new file mode 100644 index 000000000..891c802d7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jboss/tasks/main.yml @@ -0,0 +1,11 @@ +--- +#################################################################### +# 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 + +- import_tasks: jboss.yml diff --git a/ansible_collections/community/general/tests/integration/targets/jira/aliases b/ansible_collections/community/general/tests/integration/targets/jira/aliases new file mode 100644 index 000000000..9c3dc5670 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jira/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 +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/jira/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/jira/tasks/main.yml new file mode 100644 index 000000000..129070871 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jira/tasks/main.yml @@ -0,0 +1,111 @@ +--- +# 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 test ticket + community.general.jira: + uri: "{{ uri }}" + username: "{{ user }}" + password: "{{ pasw }}" + project: "{{ proj }}" + operation: create + summary: test ticket + description: bla bla bla + issuetype: Task + register: issue +- debug: + msg: Issue={{ issue }} +- name: assert test ticket + assert: + that: + - issue is changed + - issue.meta.key.startswith(proj) + +- name: add comment bleep bleep + community.general.jira: + uri: "{{ uri }}" + username: "{{ user }}" + password: "{{ pasw }}" + issue: "{{ issue.meta.key }}" + operation: comment + comment: bleep bleep! + register: comment_bleep_bleep +- name: assert comment bleep bleep + assert: + that: + - comment_bleep_bleep is changed + - comment_bleep_bleep.meta.body == "bleep bleep!" + - comment_bleep_bleep.meta.body != None + +- name: transition -> In Progress with comment + community.general.jira: + uri: "{{ uri }}" + username: "{{ user }}" + password: "{{ pasw }}" + issue: "{{ issue.meta.key }}" + operation: transition + status: Start Progress + comment: -> in progress + register: transition_inprog +- name: assert transition -> In Progress with comment + assert: + that: + - transition_inprog is changed + +- name: change assignee + community.general.jira: + uri: "{{ uri }}" + username: "{{ user }}" + password: "{{ pasw }}" + issue: "{{ issue.meta.key }}" + operation: edit + account_id: "{{ user2 }}" + register: assign +- name: assert change assignee + assert: + that: + - assign is changed + +- name: Worklog on issue + community.general.jira: + uri: '{{ server }}' + username: '{{ user }}' + password: '{{ pass }}' + issue: '{{ issue.meta.key }}' + operation: worklog + comment: Worklog + fields: + timeSpentSeconds: 1200 + register: worklog +- name: assert worklog -> with comment + assert: + that: + - worklog is changed + - worklog.meta.comment == 'Worklog' + - worklog.meta.timeSpentSeconds == 1200 + +- name: transition -> Resolved with comment + community.general.jira: + uri: "{{ uri }}" + username: "{{ user }}" + password: "{{ pasw }}" + issue: "{{ issue.meta.key }}" + operation: transition + status: Resolve Issue + comment: -> resolved + account_id: "{{ user1 }}" + fields: + resolution: + name: Done + description: wakawakawakawaka + register: transition_resolved +- name: assert transition -> Resolved with comment + assert: + that: + - transition_resolved is changed + +- debug: + msg: + - Issue = {{ issue.meta.key }} + - URL = {{ issue.meta.self }} diff --git a/ansible_collections/community/general/tests/integration/targets/jira/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/jira/vars/main.yml new file mode 100644 index 000000000..781fde8ca --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/jira/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 + +uri: https://xxxx.atlassian.net/ +user: xxx@xxxx.xxx +pasw: supersecret +proj: ABC +user1: 6574474636373822y7338 +user2: 6574474636373822y73959696 diff --git a/ansible_collections/community/general/tests/integration/targets/kdeconfig/aliases b/ansible_collections/community/general/tests/integration/targets/kdeconfig/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kdeconfig/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/kdeconfig/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/kdeconfig/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kdeconfig/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/kdeconfig/tasks/files/kwriteconf_fake b/ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/files/kwriteconf_fake new file mode 100755 index 000000000..c29627257 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/files/kwriteconf_fake @@ -0,0 +1,38 @@ +#!/bin/sh + +# Copyright (c) 2023, Salvatore Mesoraca +# 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 + +# This script is not supposed to correctly emulate +# kwriteconf output format. +# It only tries to emulate its behaviour from the +# point of view of the Ansible module. +# Which is: write something that depends on the arguments +# to the output file, unless we already wrote that before. + +set -e + +args="" +prev_was_file=0 +for var in "$@"; do + if [ $prev_was_file -eq 1 ]; then + fname="$var" + prev_was_file=0 + else + args="$args $var" + fi + if [ "$var" = "--file" ]; then + prev_was_file=1 + fi +done + +if [ "x$fname" = "x" ]; then + exit 1 +fi + +if [ -e "$fname" ]; then + grep -qF "$args" "$fname" && exit 0 +fi + +echo "$args" >> "$fname" diff --git a/ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/main.yml new file mode 100644 index 000000000..790bb378d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kdeconfig/tasks/main.yml @@ -0,0 +1,369 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2023, Salvatore Mesoraca +# 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: Set paths + set_fact: + output_file: "{{ remote_tmp_dir }}/kdeconf" + kwriteconf_fake: "{{ remote_tmp_dir }}/kwriteconf" + +- name: Install fake kwriteconf + copy: + dest: "{{ kwriteconf_fake }}" + src: kwriteconf_fake + mode: 0755 + +- name: Simple test + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + value: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_simple + ignore_errors: true + +- name: Simple test - checks + assert: + that: + - result_simple is changed + - result_simple is not failed + +- name: Simple test - idempotence + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + value: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_simple_idem + ignore_errors: true + +- name: Simple test - idempotence - checks + assert: + that: + - result_simple_idem is not changed + - result_simple_idem is not failed + +- name: Reset + file: + path: "{{ output_file }}" + state: absent + +- name: Group and groups are mutually exclusive + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + groups: [test2] + key: test1 + value: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_group_mutex + ignore_errors: true + +- name: Group and groups are mutually exclusive - checks + assert: + that: + - result_group_mutex is not changed + - result_group_mutex is failed + - "result_group_mutex.msg == 'parameters are mutually exclusive: group|groups found in values'" + +- name: value and bool_value are mutually exclusive + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + value: test2 + bool_value: true + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_val_mutex + ignore_errors: true + +- name: value and bool_value are mutually exclusive - checks + assert: + that: + - result_val_mutex is not changed + - result_val_mutex is failed + - "result_val_mutex.msg == 'parameters are mutually exclusive: value|bool_value found in values'" + +- name: bool_value must be bool + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + bool_value: thisisastring + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_val_bool + ignore_errors: true + +- name: bool_value must be bool - checks + assert: + that: + - result_val_bool is not changed + - result_val_bool is failed + - "'is not a valid boolean' in result_val_bool.msg" + +- name: Multiple groups test + kdeconfig: + path: "{{ output_file }}" + values: + - groups: + - test + - test1 + - test2 + key: test3 + value: test4 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_groups + ignore_errors: true + +- name: Multiple groups test - checks + assert: + that: + - result_groups is changed + - result_groups is not failed + +- name: Multiple groups test - idempotence + kdeconfig: + path: "{{ output_file }}" + values: + - groups: + - test + - test1 + - test2 + key: test3 + value: test4 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_groups_idem + ignore_errors: true + +- name: Multiple groups test - idempotence - checks + assert: + that: + - result_groups_idem is not changed + - result_groups_idem is not failed + +- name: Reset + file: + path: "{{ output_file }}" + state: absent + +- name: Bool test + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + bool_value: true + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_bool + ignore_errors: true + +- name: Simple test - checks + assert: + that: + - result_bool is changed + - result_bool is not failed + +- name: Bool test - idempotence + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + bool_value: on + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_bool_idem + ignore_errors: true + +- name: Bool test - idempotence - checks + assert: + that: + - result_bool_idem is not changed + - result_bool_idem is not failed + +- name: Reset + file: + path: "{{ output_file }}" + state: absent + +- name: check_mode test + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + value: test2 + - groups: [testx, testy] + key: testz + bool_value: on + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_checkmode + ignore_errors: true + check_mode: true + diff: true + +- name: check_mode test file contents + slurp: + src: "{{ output_file }}" + register: check_mode_contents + ignore_errors: true + +- name: check_mode test - checks + assert: + that: + - result_checkmode is changed + - result_checkmode is not failed + - check_mode_contents is failed + +- name: check_mode test - apply + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + value: test2 + - groups: [testx, testy] + key: testz + bool_value: on + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_checkmode_apply + ignore_errors: true + check_mode: false + diff: true + +- name: check_mode test - apply - checks + assert: + that: + - result_checkmode_apply is changed + - result_checkmode_apply is not failed + - "result_checkmode_apply['diff']['after'] == result_checkmode['diff']['after']" + - "result_checkmode_apply['diff']['before'] == result_checkmode['diff']['before']" + +- name: check_mode test - idempotence + kdeconfig: + path: "{{ output_file }}" + values: + - group: test + key: test1 + value: test2 + - groups: [testx, testy] + key: testz + bool_value: on + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_checkmode2 + ignore_errors: true + check_mode: true + +- name: check_mode test - idempotence - checks + assert: + that: + - result_checkmode2 is not changed + - result_checkmode2 is not failed + +- name: Reset + file: + path: "{{ output_file }}" + state: absent + +- name: Unicode test + kdeconfig: + path: "{{ output_file }}" + values: + - group: tesòt + key: testè1 + value: testù2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_unicode + ignore_errors: true + +- name: Unicode test - checks + assert: + that: + - result_unicode is changed + - result_unicode is not failed + +- name: Reset + file: + path: "{{ output_file }}" + state: absent + +- name: Missing groups + kdeconfig: + path: "{{ output_file }}" + values: + - key: test1 + value: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_mgroup + ignore_errors: true + +- name: Missing groups - checks + assert: + that: + - result_mgroup is not changed + - result_mgroup is failed + - "result_mgroup.msg == 'one of the following is required: group, groups found in values'" + +- name: Missing key + kdeconfig: + path: "{{ output_file }}" + values: + - group: test1 + value: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_mkey + ignore_errors: true + +- name: Missing key - checks + assert: + that: + - result_mkey is not changed + - result_mkey is failed + - "result_mkey.msg == 'missing required arguments: key found in values'" + +- name: Missing value + kdeconfig: + path: "{{ output_file }}" + values: + - group: test1 + key: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_mvalue + ignore_errors: true + +- name: Missing value - checks + assert: + that: + - result_mvalue is not changed + - result_mvalue is failed + - "result_mvalue.msg == 'one of the following is required: value, bool_value found in values'" + +- name: Empty key + kdeconfig: + path: "{{ output_file }}" + values: + - group: test1 + key: '' + value: test2 + kwriteconfig_path: "{{ kwriteconf_fake }}" + register: result_ekey + ignore_errors: true + +- name: Empty key - checks + assert: + that: + - result_ekey is not changed + - result_ekey is failed + - "result_ekey.msg == \"'key' cannot be empty\"" diff --git a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases new file mode 100644 index 000000000..afda346c4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/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/1 diff --git a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/files/blacklist b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/files/blacklist new file mode 100644 index 000000000..eeaf32246 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/files/blacklist @@ -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 + +blacklist aaaa +blacklist bbbb +blacklist cccc diff --git a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/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/kernel_blacklist/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml new file mode 100644 index 000000000..e169d5479 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml @@ -0,0 +1,111 @@ +--- +# 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: set destination filename + set_fact: + bl_file: '{{ remote_tmp_dir }}/blacklist-ansible.conf' + +- name: copy blacklist file + copy: + src: 'files/blacklist' + dest: '{{ bl_file }}' + +- name: Original stat + stat: + path: '{{ bl_file }}' + register: orig_stat + +- name: remove non-existing item from list + community.general.kernel_blacklist: + blacklist_file: '{{ bl_file }}' + state: absent + name: zzzz + register: bl_test_1 + +- name: add existing item from list + community.general.kernel_blacklist: + blacklist_file: '{{ bl_file }}' + state: present + name: bbbb + register: bl_test_1a + +- name: stat_test_1 + stat: + path: '{{ bl_file }}' + register: stat_test_1 + +- 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 + 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 + + blacklist aaaa + blacklist bbbb + blacklist cccc + +- name: add new item to list + community.general.kernel_blacklist: + blacklist_file: '{{ bl_file }}' + state: present + name: dddd + register: bl_test_2 + +- name: slurp_test_2 + slurp: + src: '{{ bl_file }}' + register: slurp_test_2 + +- name: assert element is added + assert: + that: + - 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 + + blacklist aaaa + blacklist bbbb + blacklist cccc + blacklist dddd + +- name: remove item from list + community.general.kernel_blacklist: + blacklist_file: '{{ bl_file }}' + state: absent + name: bbbb + register: bl_test_3 + +- name: slurp_test_3 + slurp: + src: '{{ bl_file }}' + register: slurp_test_3 + +- name: assert element is added + assert: + that: + - 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 + + blacklist aaaa + blacklist cccc + blacklist dddd diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/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_authorization_scope/readme.adoc b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc new file mode 100644 index 000000000..1941e54ef --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/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= -e KEYCLOAK_ADMIN= -e KEYCLOAK_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 completly. 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_authorization_scope/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/tasks/main.yml new file mode 100644 index 000000000..504a24a01 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/tasks/main.yml @@ -0,0 +1,234 @@ +--- +# 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 an authorization scope (check mode) + 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 }}" + check_mode: true + diff: true + register: result + +- name: Assert that authorization scope was not created in check mode + assert: + that: + - result is changed + - result.end_state == {} + - result.msg == 'Authorization scope would be created' + - result.diff.before == {} + - result.diff.after.name == 'file:delete' + - result.diff.after.displayName == 'File delete' + - result.diff.after.iconUri == 'http://localhost/icon.png' + +- name: 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:delete" + display_name: "File delete" + icon_uri: "http://localhost/icon.png" + client_id: "{{ client_id }}" + realm: "{{ realm }}" + register: result + +- name: Assert that authorization scope was created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "file:delete" + - result.end_state.iconUri == "http://localhost/icon.png" + - result.end_state.displayName == "File delete" + +- name: Create authorization scope (test for idempotency) + 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: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state != {} + - result.end_state.name == "file:delete" + - result.end_state.iconUri == "http://localhost/icon.png" + - result.end_state.displayName == "File delete" + +- name: Authorization scope update (check mode) + 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" + client_id: "{{ client_id }}" + realm: "{{ realm }}" + diff: true + check_mode: true + register: result + +- name: Assert that authorization scope was not updated in check mode + assert: + that: + - result is changed + - result.msg == 'Authorization scope would be updated' + - result.diff.before.displayName == 'File delete' + - result.diff.before.iconUri == 'http://localhost/icon.png' + - result.diff.after.displayName == '' + - result.diff.after.iconUri == '' + +- name: Authorization scope update (remove optional parameters) + 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" + client_id: "{{ client_id }}" + realm: "{{ realm }}" + register: result + +- name: Assert that optional parameters have been removed + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "file:delete" + - result.end_state.iconUri == "" + - result.end_state.displayName == "" + +- name: Authorization scope update (test for idempotency) + 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" + client_id: "{{ client_id }}" + realm: "{{ realm }}" + register: result + +- name: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state != {} + - result.end_state.name == "file:delete" + - result.end_state.iconUri == "" + - result.end_state.displayName == "" + +- name: Authorization scope remove (check mode) + community.general.keycloak_authz_authorization_scope: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + state: absent + name: "file:delete" + client_id: "{{ client_id }}" + realm: "{{ realm }}" + diff: true + check_mode: true + register: result + +- name: Assert that authorization scope has not been removed in check mode + assert: + that: + - result is changed + - result.msg == 'Authorization scope would be removed' + - result.diff.before.name == 'file:delete' + - result.diff.after == {} + +- name: Authorization scope remove + community.general.keycloak_authz_authorization_scope: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + state: absent + name: "file:delete" + client_id: "{{ client_id }}" + realm: "{{ realm }}" + register: result + +- name: Assert that authorization scope has been removed + assert: + that: + - result is changed + - result.end_state == {} + +- name: Authorization scope remove (test for idempotency) + community.general.keycloak_authz_authorization_scope: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + state: absent + name: "file:delete" + 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_authorization_scope/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/vars/main.yml new file mode 100644 index 000000000..c1d5fc983 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/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 new file mode 100644 index 000000000..d8bcc08ec --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md @@ -0,0 +1,17 @@ + + +The integration test can be performed as follows: + +``` +# 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 + +# 2. Run the integration tests: +ansible-test integration keycloak_client --allow-unsupported -v +``` 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 new file mode 100644 index 000000000..5e14e9aac --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/docker-compose.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 + +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 new file mode 100644 index 000000000..513d5836b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml @@ -0,0 +1,63 @@ +--- +# 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: Delete realm + community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + 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 + +- 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}}' + 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}}' + register: desire_client_when_present_and_same + +- name: Check client again with same props + community.general.keycloak_client: "{{ auth_args | combine(call_args) }}" + 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) + assert: + that: + - desire_client_when_present_and_same is not changed + - check_client_when_present_and_same is not 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 new file mode 100644 index 000000000..53ba35fca --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml @@ -0,0 +1,61 @@ +--- +# 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 +client_id: myclient +role: myrole +description_1: desc 1 +description_2: desc 2 + +auth_args: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + +redirect_uris1: + - "http://example.c.com/" + - "http://example.b.com/" + - "http://example.a.com/" + +client_attributes1: {"backchannel.logout.session.required": true, "backchannel.logout.revoke.offline.tokens": false} + +protocol_mappers1: + - name: 'email' + protocol: 'openid-connect' + protocolMapper: 'oidc-usermodel-property-mapper' + config: + "claim.name": "email" + "user.attribute": "email" + "jsonType.label": "String" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" + + - name: 'email_verified' + protocol: 'openid-connect' + protocolMapper: 'oidc-usermodel-property-mapper' + config: + "claim.name": "email_verified" + "user.attribute": "emailVerified" + "jsonType.label": "boolean" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" + + - name: 'family_name' + protocol: 'openid-connect' + protocolMapper: 'oidc-usermodel-property-mapper' + config: + "claim.name": "family_name" + "user.attribute": "lastName" + "jsonType.label": "String" + "id.token.claim": "true" + "access.token.claim": "true" + "userinfo.token.claim": "true" diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/README.md new file mode 100644 index 000000000..3f3685f9b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/README.md @@ -0,0 +1,16 @@ + + +The integration test can be performed as follows: + +``` +# 1. Start docker-compose: +docker-compose -f tests/integration/targets/keycloak_clientscope_type/docker-compose.yml down +docker-compose -f tests/integration/targets/keycloak_clientscope_type/docker-compose.yml up -d + +# 2. Run the integration tests: +ansible-test integration keycloak_clientscope_type --allow-unsupported -v +``` diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/docker-compose.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/docker-compose.yml new file mode 100644 index 000000000..b73ddff16 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/docker-compose.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 + +version: '3.4' + +services: + keycloak: + image: quay.io/keycloak/keycloak:21.0.2 + ports: + - 8080:8080 + environment: + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: password + command: start-dev diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/tasks/main.yml new file mode 100644 index 000000000..76daace73 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/tasks/main.yml @@ -0,0 +1,164 @@ +--- +# 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 + + +# Fixtures +- name: Create keycloak realm + community.general.keycloak_realm: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "" + state: present + enabled: true + +- name: Create 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: present + enabled: true + +- name: Create a scope1 client scope + community.general.keycloak_clientscope: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: scope1 + description: "test 1" + protocol: openid-connect + +- name: Create a scope2 client scope + community.general.keycloak_clientscope: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: scope2 + description: "test 2" + protocol: openid-connect + +### Tests +### Realm +- name: adjust client-scope types in realm + community.general.keycloak_clientscope_type: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + default_clientscopes: ['scope1', 'scope2'] + optional_clientscopes: [] + register: result + +- name: Assert that client scope types are set + assert: + that: + - result is changed + - result.end_state != {} + - '"scope1" in result.end_state.default_clientscopes' + - '"scope2" in result.end_state.default_clientscopes' + - result.end_state.default_clientscopes|length == 2 + - result.end_state.optional_clientscopes|length == 0 + +- name: adjust client-scope types in realm again + community.general.keycloak_clientscope_type: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + default_clientscopes: ['scope1', 'scope2'] + optional_clientscopes: [] + register: result + failed_when: result is changed + +- name: adjust client-scope types in realm move scope 2 to optional + community.general.keycloak_clientscope_type: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + default_clientscopes: ['scope1'] + optional_clientscopes: ['scope2'] + register: result + +- name: Assert that client scope types are set + assert: + that: + - result is changed + - result.end_state != {} + - '"scope1" in result.end_state.default_clientscopes' + - '"scope2" in result.end_state.optional_clientscopes' + - result.end_state.default_clientscopes|length == 1 + - result.end_state.optional_clientscopes|length == 1 + +### Client +- name: adjust client-scope types in client + community.general.keycloak_clientscope_type: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + client_id: "{{ client_id }}" + default_clientscopes: ['scope1', 'scope2'] + optional_clientscopes: [] + register: result + +- name: Assert that client scope types are set + assert: + that: + - result is changed + - result.end_state != {} + - '"scope1" in result.end_state.default_clientscopes' + - '"scope2" in result.end_state.default_clientscopes' + - result.end_state.default_clientscopes|length == 2 + - result.end_state.optional_clientscopes|length == 0 + +- name: adjust client-scope types in client again + community.general.keycloak_clientscope_type: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + client_id: "{{ client_id }}" + default_clientscopes: ['scope1', 'scope2'] + optional_clientscopes: [] + register: result + failed_when: result is changed + +- name: adjust client-scope types in client move scope 2 to optional + community.general.keycloak_clientscope_type: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + client_id: "{{ client_id }}" + default_clientscopes: ['scope1'] + optional_clientscopes: ['scope2'] + register: result + +- name: Assert that client scope types are set + assert: + that: + - result is changed + - result.end_state != {} + - '"scope1" in result.end_state.default_clientscopes' + - '"scope2" in result.end_state.optional_clientscopes' + - result.end_state.default_clientscopes|length == 1 + - result.end_state.optional_clientscopes|length == 1 diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/vars/main.yml new file mode 100644 index 000000000..7efd2b04e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientscope_type/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 +admin_realm: master +admin_user: admin +admin_password: password +realm: clientscope-type-realm +client_id: clientscope-type-client diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/README.md new file mode 100644 index 000000000..fb721801d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/README.md @@ -0,0 +1,17 @@ + + +The integration test can be performed as follows: + +``` +# 1. Start docker-compose: +docker-compose -f tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml stop +docker-compose -f tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml rm -f -v +docker-compose -f tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml up -d + +# 2. Run the integration tests: +ansible-test integration keycloak_clientsecret_info --allow-unsupported -v +``` diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/docker-compose.yml new file mode 100644 index 000000000..5e14e9aac --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/docker-compose.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 + +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_clientsecret_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/tasks/main.yml new file mode 100644 index 000000000..a0cacf188 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/tasks/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 + +- name: Create realm + community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + id: "{{ realm }}" + realm: "{{ realm }}" + state: present + +- name: Keycloak Client + community.general.keycloak_client: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + state: present + register: client + +- name: Keycloak Client fetch clientsecret by client_id + community.general.keycloak_clientsecret_info: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + register: fetch_by_client_id_result + +- name: Assert that the client secret was retrieved + assert: + that: + - fetch_by_client_id_result.clientsecret_info.type == "secret" + - "{{ fetch_by_client_id_result.clientsecret_info.value | length }} >= 32" + +- name: Keycloak Client fetch clientsecret by id + community.general.keycloak_clientsecret_info: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + id: "{{ client.end_state.id }}" + register: fetch_by_id_result + +- name: Assert that the same client secret was retrieved both times + assert: + that: + - fetch_by_id_result.clientsecret_info.value == fetch_by_client_id_result.clientsecret_info.value diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/vars/main.yml new file mode 100644 index 000000000..8c913705f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_info/vars/main.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 + +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 + +auth_args: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/README.md new file mode 100644 index 000000000..08251b4c5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/README.md @@ -0,0 +1,17 @@ + + +The integration test can be performed as follows: + +``` +# 1. Start docker-compose: +docker-compose -f tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml stop +docker-compose -f tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml rm -f -v +docker-compose -f tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml up -d + +# 2. Run the integration tests: +ansible-test integration keycloak_clientsecret_regenerate --allow-unsupported -v +``` diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.yml new file mode 100644 index 000000000..5e14e9aac --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/docker-compose.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 + +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_clientsecret_regenerate/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/tasks/main.yml new file mode 100644 index 000000000..9bd52698a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/tasks/main.yml @@ -0,0 +1,49 @@ +--- +# 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 realm + community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + id: "{{ realm }}" + realm: "{{ realm }}" + state: present + +- name: Keycloak Client + community.general.keycloak_client: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + state: present + register: client + +- name: Keycloak Client regenerate clientsecret by client_id + community.general.keycloak_clientsecret_regenerate: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + client_id: "{{ client_id }}" + register: regenerate_by_client_id + +- name: Assert that the client secret was retrieved + assert: + that: + - regenerate_by_client_id.end_state.type == "secret" + - "{{ regenerate_by_client_id.end_state.value | length }} >= 32" + +- name: Keycloak Client regenerate clientsecret by id + community.general.keycloak_clientsecret_regenerate: "{{ auth_args | combine(call_args) }}" + vars: + call_args: + realm: "{{ realm }}" + id: "{{ client.end_state.id }}" + register: regenerate_by_id + +- name: Assert that client secret was regenerated + assert: + that: + - "{{ regenerate_by_id.end_state.value | length }} >= 32" + - regenerate_by_id.end_state.value != regenerate_by_client_id.end_state.value diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.yml new file mode 100644 index 000000000..8c913705f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_clientsecret_regenerate/vars/main.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 + +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 + +auth_args: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_group/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group/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_group/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_group/meta/main.yml new file mode 100644 index 000000000..5769ff1cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group/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_docker 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 new file mode 100644 index 000000000..1941e54ef --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group/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= -e KEYCLOAK_ADMIN= -e KEYCLOAK_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 completly. 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/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_group/tasks/main.yml new file mode 100644 index 000000000..8b115e3a2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group/tasks/main.yml @@ -0,0 +1,527 @@ +--- +# 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: Start container + community.docker.docker_container: + name: mykeycloak + image: "quay.io/keycloak/keycloak:20.0.2" + command: start-dev + env: + KC_HTTP_RELATIVE_PATH: /auth + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: password + ports: + - "8080:8080" + detach: true + auto_remove: true + memory: 2200M + +- name: Check default ports + ansible.builtin.wait_for: + host: "localhost" + port: "8080" + state: started # Port should be open + delay: 30 # Wait before first check + timeout: 50 # Stop checking after timeout (sec) + +- name: Create a keycloak group + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: test-group + state: present + register: result + retries: 3 + delay: 20 + until: result is not failed + +- name: Assert group was created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "test-group" + - result.end_state.path == "/test-group" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- set_fact: + test_group_id: "{{ result.end_state.id }}" + +- name: Group creation rerun (test for idempotency) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: test-group + state: present + register: result + +- name: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state != {} + - result.end_state.name == "test-group" + - result.end_state.path == "/test-group" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: Update the name of a keycloak group + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "{{ test_group_id }}" + name: new-test-group + state: present + register: result + +- name: Assert that group name was updated + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "new-test-group" + - result.end_state.path == "/new-test-group" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: Delete a keycloak group by id + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "{{ test_group_id }}" + state: absent + register: result + +- name: Assert that group was deleted + assert: + that: + - result is changed + - result.end_state == {} + +- name: Redo group deletion (check for idempotency) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "{{ test_group_id }}" + state: absent + register: result + +- name: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state == {} + +- name: Create a keycloak group with some custom attributes + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: my-new_group + attributes: + attrib1: value1 + attrib2: value2 + attrib3: + - item1 + - item2 + register: result + +- name: Assert that group was correctly created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "my-new_group" + - result.end_state.path == "/my-new_group" + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + - result.end_state.attributes != {} + - result.end_state.attributes.attrib1 == ["value1"] + - result.end_state.attributes.attrib2 == ["value2"] + - result.end_state.attributes.attrib3 == ["item1", "item2"] + +- name: Delete a keycloak group based on name + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: my-new_group + state: absent + register: result + +- name: Assert that group was deleted + assert: + that: + - result is changed + - result.end_state == {} + +## subgroup tests +## we already testet this so no asserts for this +- name: Create a new base group for subgroup testing (test setup) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: rootgrp + register: subgrp_basegrp_result + +- name: Create a subgroup using parent id + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subgrp1 + parents: + - id: "{{ subgrp_basegrp_result.end_state.id }}" + register: result + +- name: Assert that subgroup was correctly created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "subgrp1" + - result.end_state.path == "/rootgrp/subgrp1" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: Recreate a subgroup using parent id (test idempotency) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subgrp1 + parents: + - id: "{{ subgrp_basegrp_result.end_state.id }}" + register: result + +- name: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state != {} + - result.end_state.name == "subgrp1" + - result.end_state.path == "/rootgrp/subgrp1" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: Changing name of existing group + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "{{ result.end_state.id }}" + name: new-subgrp1 + parents: + - id: "{{ subgrp_basegrp_result.end_state.id }}" + register: result + +- name: Assert that subgroup name has changed correctly + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "new-subgrp1" + - result.end_state.path == "/rootgrp/new-subgrp1" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: Create a subgroup using parent name + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subgrp2 + parents: + - name: rootgrp + register: result + +- name: Assert that subgroup was correctly created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "subgrp2" + - result.end_state.path == "/rootgrp/subgrp2" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: Recreate a subgroup using parent name (test idempotency) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subgrp2 + parents: + - name: rootgrp + register: result + +- name: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state != {} + - result.end_state.name == "subgrp2" + - result.end_state.path == "/rootgrp/subgrp2" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +## subgroup of subgroup tests +- name: Create a subgroup of a subgroup using parent names (complete parent chain) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subsubgrp + parents: + - name: rootgrp + - name: subgrp2 + register: result + +- name: Assert subgroup of subgroup was created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "subsubgrp" + - result.end_state.path == "/rootgrp/subgrp2/subsubgrp" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: ReCreate a subgroup of a subgroup using parent names (test idempotency) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subsubgrp + parents: + - name: rootgrp + - name: subgrp2 + register: result_subsubgrp + +- name: Assert that nothing has changed + assert: + that: + - result_subsubgrp is not changed + - result_subsubgrp.end_state != {} + - result_subsubgrp.end_state.name == "subsubgrp" + - result_subsubgrp.end_state.path == "/rootgrp/subgrp2/subsubgrp" + - result_subsubgrp.end_state.attributes == {} + - result_subsubgrp.end_state.clientRoles == {} + - result_subsubgrp.end_state.realmRoles == [] + - result_subsubgrp.end_state.subGroups == [] + +- name: Create a subgroup of a subgroup using direct parent id (incomplete parent chain) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subsubsubgrp + parents: + - id: "{{ result_subsubgrp.end_state.id }}" + register: result + +- name: Assert subgroup of subgroup was created + assert: + that: + - result is changed + - result.end_state != {} + - result.end_state.name == "subsubsubgrp" + - result.end_state.path == "/rootgrp/subgrp2/subsubgrp/subsubsubgrp" + - result.end_state.attributes == {} + - result.end_state.clientRoles == {} + - result.end_state.realmRoles == [] + - result.end_state.subGroups == [] + +- name: ReCreate a subgroup of a subgroup using direct parent id (test idempotency) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: subsubsubgrp + parents: + - id: "{{ result_subsubgrp.end_state.id }}" + register: result_subsubsubgrp + +- name: Assert that nothing changed + assert: + that: + - result_subsubsubgrp is not changed + - result_subsubsubgrp.end_state != {} + - result_subsubsubgrp.end_state.name == "subsubsubgrp" + - result_subsubsubgrp.end_state.path == "/rootgrp/subgrp2/subsubgrp/subsubsubgrp" + - result_subsubsubgrp.end_state.attributes == {} + - result_subsubsubgrp.end_state.clientRoles == {} + - result_subsubsubgrp.end_state.realmRoles == [] + - result_subsubsubgrp.end_state.subGroups == [] + +## subgroup deletion tests +## note: in principle we already have tested group deletion in general +## enough already, but what makes it interesting here again is to +## see it works also properly for subgroups and groups with subgroups +- name: Deleting a subgroup by id (no parents needed) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "{{ result_subsubsubgrp.end_state.id }}" + state: absent + register: result + +- name: Assert that subgroup was deleted + assert: + that: + - result is changed + - result.end_state == {} + +- name: Redo subgroup deletion (idempotency test) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + id: "{{ result_subsubsubgrp.end_state.id }}" + state: absent + register: result + +- name: Assert that nothing changed + assert: + that: + - result is not changed + - result.end_state == {} + +- name: Deleting a subgroup by name + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: new-subgrp1 + parents: + - name: rootgrp + state: absent + register: result + +- name: Assert that subgroup was deleted + assert: + that: + - result is changed + - result.end_state == {} + +- name: Redo deleting a subgroup by name (idempotency test) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: new-subgrp1 + parents: + - name: rootgrp + state: absent + register: result + +- name: Assert that nothing has changed + assert: + that: + - result is not changed + - result.end_state == {} + +- name: Delete keycloak group which has subgroups + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: rootgrp + state: absent + register: result + +- name: Assert that group was deleted + assert: + that: + - result is changed + - result.end_state == {} + +- name: Redo delete keycloak group which has subgroups (idempotency test) + community.general.keycloak_group: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: rootgrp + state: absent + register: result + +- name: Assert that group was deleted + assert: + that: + - result is not changed + - result.end_state == {} diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_group/vars/main.yml new file mode 100644 index 000000000..e8aeb4f3f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group/vars/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 + +url: http://localhost:8080/auth +admin_realm: master +admin_user: admin +admin_password: password +realm: master diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/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_identity_provider/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml new file mode 100644 index 000000000..79ba33049 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml @@ -0,0 +1,175 @@ +--- +# 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 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 identity provider + 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 }}" + display_name: OpenID Connect IdP + enabled: true + provider_id: oidc + config: + issuer: https://idp.example.com + authorizationUrl: https://idp.example.com/auth + tokenUrl: https://idp.example.com/token + userInfoUrl: https://idp.example.com/userinfo + clientAuthMethod: client_secret_post + clientId: clientid + clientSecret: clientsecret + syncMode: FORCE + mappers: + - name: "first_name" + identityProviderAlias: "oidc-idp" + identityProviderMapper: "oidc-user-attribute-idp-mapper" + config: + claim: "first_name" + user.attribute: "first_name" + syncMode: "INHERIT" + - name: "last_name" + identityProviderAlias: "oidc-idp" + identityProviderMapper: "oidc-user-attribute-idp-mapper" + config: + claim: "last_name" + user.attribute: "last_name" + syncMode: "INHERIT" + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert identity provider created + assert: + that: + - result is changed + - result.existing == {} + - result.end_state.alias == "{{ idp }}" + - result.end_state.mappers != [] + +- name: Update existing identity provider (no change) + 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 }}" + enabled: true + provider_id: oidc + config: + issuer: https://idp.example.com + authorizationUrl: https://idp.example.com/auth + tokenUrl: https://idp.example.com/token + userInfoUrl: https://idp.example.com/userinfo + clientAuthMethod: client_secret_post + clientId: clientid + clientSecret: "**********" + syncMode: FORCE + mappers: + - name: "first_name" + identityProviderAlias: "oidc-idp" + identityProviderMapper: "oidc-user-attribute-idp-mapper" + config: + claim: "first_name" + user.attribute: "first_name" + syncMode: "INHERIT" + - name: "last_name" + identityProviderAlias: "oidc-idp" + identityProviderMapper: "oidc-user-attribute-idp-mapper" + config: + claim: "last_name" + user.attribute: "last_name" + syncMode: "INHERIT" + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert identity provider unchanged + assert: + that: + - result is not changed + +- name: Update existing identity provider (with change) + 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 }}" + enabled: false + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert identity provider updated + assert: + that: + - result is changed + - result.existing.enabled == true + - result.end_state.enabled == false + +- name: Delete existing identity provider + 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: absent + register: result + +- name: Debug + debug: + var: result + +- name: Assert identity provider deleted + assert: + that: + - result is changed + - result.end_state == {} + +- name: Delete absent identity provider + 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: absent + register: result + +- name: Debug + debug: + var: result + +- name: Assert identity provider unchanged + assert: + that: + - result is not changed + - result.end_state == {} diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/vars/main.yml new file mode 100644 index 000000000..6d2078ca0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/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: myrealm +idp: myidp diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_role/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_role/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_role/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_role/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml new file mode 100644 index 000000000..61b62629a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml @@ -0,0 +1,250 @@ +--- +# 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 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 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: present + register: client + +- 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 + register: result + +- name: Debug + debug: + var: result + +- name: Assert realm role created + assert: + that: + - result is changed + - result.existing == {} + - result.end_state.name == "{{ role }}" + - result.end_state.containerId == "{{ realm }}" + +- name: Create existing 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 + register: result + +- name: Debug + debug: + var: result + +- name: Assert realm role unchanged + assert: + that: + - result is not changed + +- name: Update 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_2 }}" + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert realm role updated + assert: + that: + - result is changed + - result.existing.description == "{{ description_1 }}" + - result.end_state.description == "{{ description_2 }}" + +- name: Delete existing 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 }}" + 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 + community.general.keycloak_role: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + name: "{{ role }}" + state: absent + register: result + +- name: Debug + debug: + var: result + +- name: Assert realm role unchanged + assert: + that: + - result is not changed + - result.end_state == {} + +- 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: "{{ role }}" + description: "{{ description_1 }}" + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert client role created + assert: + that: + - result is changed + - result.existing == {} + - result.end_state.name == "{{ role }}" + - result.end_state.containerId == "{{ client.end_state.id }}" + +- name: Create existing 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: "{{ role }}" + description: "{{ description_1 }}" + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert client role unchanged + assert: + that: + - result is not changed + +- name: Update 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: "{{ role }}" + description: "{{ description_2 }}" + state: present + register: result + +- name: Debug + debug: + var: result + +- name: Assert client role updated + assert: + that: + - result is changed + - result.existing.description == "{{ description_1 }}" + - result.end_state.description == "{{ description_2 }}" + +- name: Delete existing 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: "{{ role }}" + 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 + 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: "{{ role }}" + state: absent + register: result + +- name: Debug + debug: + var: result + +- name: Assert client role unchanged + assert: + that: + - result is not changed + - result.end_state == {} 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 new file mode 100644 index 000000000..b003311e0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/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 + +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 diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/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_user_federation/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/tasks/main.yml new file mode 100644 index 000000000..ae0b4bf16 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/tasks/main.yml @@ -0,0 +1,425 @@ +--- +# 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 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 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: "ldaps://ldap.example.com:636" + 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 + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation created + assert: + that: + - result is changed + - result.existing == {} + - result.end_state.name == "{{ federation }}" + +- name: Create new user federation in admin realm + community.general.keycloak_user_federation: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ admin_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: "ldaps://ldap.example.com:636" + 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 + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation created (admin realm) + assert: + that: + - result is changed + - result.existing == {} + - result.end_state.name == "{{ federation }}" + +- name: Update existing user federation (no change) + 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: "ldaps://ldap.example.com:636" + usersDn: "ou=Users,dc=example,dc=com" + authType: simple + bindDn: cn=directory reader + bindCredential: "**********" + searchScope: 1 + validatePasswordPolicy: false + trustEmail: false + useTruststoreSpi: "ldapsOnly" + connectionPooling: true + pagination: true + allowKerberosAuthentication: false + useKerberosForPasswordAuthentication: false + debug: false + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation unchanged + assert: + that: + - result is not changed + - result.existing != {} + - result.existing.name == "{{ federation }}" + - result.end_state != {} + - result.end_state.name == "{{ federation }}" + +- name: Update existing user federation (no change, admin realm) + community.general.keycloak_user_federation: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ admin_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: "ldaps://ldap.example.com:636" + usersDn: "ou=Users,dc=example,dc=com" + authType: simple + bindDn: cn=directory reader + bindCredential: "**********" + searchScope: 1 + validatePasswordPolicy: false + trustEmail: false + useTruststoreSpi: "ldapsOnly" + connectionPooling: true + pagination: true + allowKerberosAuthentication: false + useKerberosForPasswordAuthentication: false + debug: false + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation unchanged (admin realm) + assert: + that: + - result is not changed + - result.existing != {} + - result.existing.name == "{{ federation }}" + - result.end_state != {} + - result.end_state.name == "{{ federation }}" + +- name: Update existing user federation (with change) + 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: "ldaps://ldap.example.com:636" + usersDn: "ou=Users,dc=example,dc=com" + authType: simple + bindDn: cn=directory reader + bindCredential: "**********" + searchScope: 1 + validatePasswordPolicy: false + trustEmail: false + useTruststoreSpi: "ldapsOnly" + connectionPooling: true + pagination: true + allowKerberosAuthentication: false + useKerberosForPasswordAuthentication: false + debug: false + mappers: + # overwrite / update pre existing default mapper + - name: "username" + providerId: "user-attribute-ldap-mapper" + config: + ldap.attribute: ldap_user + user.model.attribute: usr + read.only: true + # create new mapper + - name: "full name" + providerId: "full-name-ldap-mapper" + providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper" + config: + ldap.full.name.attribute: cn + read.only: true + write.only: false + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation created + assert: + that: + - result is changed + - result.existing != {} + - result.existing.name == "{{ federation }}" + - result.end_state != {} + - result.end_state.name == "{{ federation }}" + +- name: Delete existing 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: absent + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation deleted + assert: + that: + - result is changed + - result.existing != {} + - result.end_state == {} + +- name: Delete absent 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: absent + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation unchanged + assert: + that: + - result is not changed + - result.existing == {} + - result.end_state == {} + +- name: Create new user federation together with mappers + 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: "ldaps://ldap.example.com:636" + 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 + mappers: + # overwrite / update pre existing default mapper + - name: "username" + providerId: "user-attribute-ldap-mapper" + config: + ldap.attribute: ldap_user + user.model.attribute: usr + read.only: true + # create new mapper + - name: "full name" + providerId: "full-name-ldap-mapper" + providerType: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper" + config: + ldap.full.name.attribute: cn + read.only: true + write.only: false + register: result + +- name: Debug + debug: + var: result + +- name: Assert user federation created + assert: + that: + - result is changed + - result.existing == {} + - result.end_state.name == "{{ federation }}" + +## no point in retesting this, just doing it to clean up introduced server changes +- name: Delete absent 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: absent diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/vars/main.yml new file mode 100644 index 000000000..acf73e2ca --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user_federation/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: myrealm +federation: myfed diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/aliases new file mode 100644 index 000000000..cdeae1417 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/aliases @@ -0,0 +1,4 @@ +# 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 +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/tasks/main.yml new file mode 100644 index 000000000..1a897ad9a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/tasks/main.yml @@ -0,0 +1,143 @@ +# 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 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 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: Map a realm role to client service account + vars: + - roles: [ {'name': '{{ role }}'} ] + community.general.keycloak_user_rolemapping: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + service_account_user_client_id: "{{ client_id }}" + roles: "{{ roles }}" + state: present + register: result + +- name: Assert realm role is assigned + assert: + that: + - result is changed + - result.end_state | selectattr("clientRole", "eq", false) | selectattr("name", "eq", "{{role}}") | list | count > 0 + +- name: Unmap a realm role from client service account + vars: + - roles: [ {'name': '{{ role }}'} ] + community.general.keycloak_user_rolemapping: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + service_account_user_client_id: "{{ client_id }}" + roles: "{{ roles }}" + state: absent + register: result + +- name: Assert realm role is unassigned + assert: + that: + - result is changed + - (result.end_state | length) == (result.existing | length) - 1 + - result.existing | selectattr("clientRole", "eq", false) | selectattr("name", "eq", "{{role}}") | list | count > 0 + - result.end_state | selectattr("clientRole", "eq", false) | selectattr("name", "eq", "{{role}}") | list | count == 0 + +- name: Delete existing 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 }}" + state: absent + +- 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: "{{ role }}" + description: "{{ description_1 }}" + state: present + +- name: Map a client role to client service account + vars: + - roles: [ {'name': '{{ role }}'} ] + community.general.keycloak_user_rolemapping: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + client_id: "{{ client_id }}" + service_account_user_client_id: "{{ client_id }}" + roles: "{{ roles }}" + state: present + register: result + +- name: Assert client role is assigned + assert: + that: + - result is changed + - result.end_state | selectattr("clientRole", "eq", true) | selectattr("name", "eq", "{{role}}") | list | count > 0 + +- name: Unmap a client role from client service account + vars: + - roles: [ {'name': '{{ role }}'} ] + community.general.keycloak_user_rolemapping: + auth_keycloak_url: "{{ url }}" + auth_realm: "{{ admin_realm }}" + auth_username: "{{ admin_user }}" + auth_password: "{{ admin_password }}" + realm: "{{ realm }}" + client_id: "{{ client_id }}" + service_account_user_client_id: "{{ client_id }}" + roles: "{{ roles }}" + state: absent + register: result + +- name: Assert client role is unassigned + assert: + that: + - result is changed + - result.end_state == [] + - result.existing | selectattr("clientRole", "eq", true) | selectattr("name", "eq", "{{role}}") | list | count > 0 diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/vars/main.yml new file mode 100644 index 000000000..385dbea44 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user_rolemapping/vars/main.yml @@ -0,0 +1,14 @@ +--- +# 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 diff --git a/ansible_collections/community/general/tests/integration/targets/keyring/aliases b/ansible_collections/community/general/tests/integration/targets/keyring/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keyring/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/keyring/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keyring/tasks/main.yml new file mode 100644 index 000000000..3833018e8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keyring/tasks/main.yml @@ -0,0 +1,99 @@ +--- +# 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 required packages for headless keyring access are installed (RPM) + ansible.builtin.package: + name: gnome-keyring + become: true + when: "'localhost' not in inventory_hostname" + +- name: Ensure keyring is installed (RPM) + ansible.builtin.dnf: + name: python3-keyring + state: present + become: true + when: ansible_facts['os_family'] == 'RedHat' + +- name: Ensure keyring is installed (pip) + ansible.builtin.pip: + name: keyring + state: present + become: true + when: ansible_facts['os_family'] != 'RedHat' + +# Set password for new account +# Expected result: success +- name: Set password for test/test1 + community.general.keyring: + service: test + username: test1 + user_password: "{{ user_password }}" + keyring_password: "{{ keyring_password }}" + register: set_password + +- name: Assert that the password has been set + ansible.builtin.assert: + that: + - set_password.msg == "Passphrase has been updated for test@test1" + +# Print out password to confirm it has been set +# Expected result: success +- name: Retrieve password for test/test1 + community.general.keyring_info: + service: test + username: test1 + keyring_password: "{{ keyring_password }}" + register: test_set_password + +- name: Assert that the password exists + ansible.builtin.assert: + that: + - test_set_password.passphrase == user_password + +# Attempt to set password again +# Expected result: success - nothing should happen +- name: Attempt to re-set password for test/test1 + community.general.keyring: + service: test + username: test1 + user_password: "{{ user_password }}" + keyring_password: "{{ keyring_password }}" + register: second_set_password + +- name: Assert that the password has not been changed + ansible.builtin.assert: + that: + - second_set_password.msg == "Passphrase already set for test@test1" + +# Delete account +# Expected result: success +- name: Delete password for test/test1 + community.general.keyring: + service: test + username: test1 + user_password: "{{ user_password }}" + keyring_password: "{{ keyring_password }}" + state: absent + register: del_password + +- name: Assert that the password has been deleted + ansible.builtin.assert: + that: + - del_password.msg == "Passphrase has been removed for test@test1" + +# Attempt to get deleted account (to confirm it has been deleted). +# Don't use `no_log` as run completes due to failed task. +# Expected result: fail +- name: Retrieve password for test/test1 + community.general.keyring_info: + service: test + username: test1 + keyring_password: "{{ keyring_password }}" + register: test_del_password + +- name: Assert that the password no longer exists + ansible.builtin.assert: + that: + - test_del_password.passphrase is not defined diff --git a/ansible_collections/community/general/tests/integration/targets/keyring/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keyring/vars/main.yml new file mode 100644 index 000000000..b4997b6d3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/keyring/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 + +keyring_password: Password123 +user_password: Test123 diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/aliases b/ansible_collections/community/general/tests/integration/targets/launchd/aliases new file mode 100644 index 000000000..a35088696 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/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 +skip/freebsd +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/files/ansible_test_service.py b/ansible_collections/community/general/tests/integration/targets/launchd/files/ansible_test_service.py new file mode 100644 index 000000000..31de6c586 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/files/ansible_test_service.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# 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 + +if __name__ == '__main__': + if sys.version_info[0] >= 3: + import http.server + import socketserver + PORT = int(sys.argv[1]) + Handler = http.server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer(("", PORT), Handler) + httpd.serve_forever() + else: + import mimetypes + mimetypes.init() + mimetypes.add_type('application/json', '.json') + import SimpleHTTPServer + SimpleHTTPServer.test() diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/main.yml new file mode 100644 index 000000000..8f5b14a59 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/main.yml @@ -0,0 +1,30 @@ +--- +#################################################################### +# 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: Test launchd module + block: + - name: Expect that launchctl exists + stat: + path: /bin/launchctl + register: launchctl_check + failed_when: + - not launchctl_check.stat.exists + + - name: Run tests + include_tasks: test.yml + with_items: + - test_unknown + - test_start_stop + - test_restart + - test_unload + - test_reload + - test_runatload + + when: ansible_os_family == 'Darwin' diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/setup.yml new file mode 100644 index 000000000..bd7134cc0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/setup.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: "[{{ item }}] Deploy test service configuration" + template: + src: "{{ launchd_service_name }}.plist.j2" + dest: "{{ launchd_plist_location }}" + become: true + +- name: install the test daemon script + copy: + src: ansible_test_service.py + dest: /usr/local/sbin/ansible_test_service + mode: '755' + +- name: rewrite shebang in the test daemon script + lineinfile: + path: /usr/local/sbin/ansible_test_service + line: "#!{{ ansible_python_interpreter | realpath }}" + insertbefore: BOF + firstmatch: true diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/teardown.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/teardown.yml new file mode 100644 index 000000000..e364056e6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/teardown.yml @@ -0,0 +1,30 @@ +--- +# 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: "[{{ item }}] Unload service" + launchd: + name: "{{ launchd_service_name }}" + state: unloaded + become: true + register: launchd_unloaded_result + +- name: "[{{ item }}] Validation" + assert: + that: + - launchd_unloaded_result is success + - launchd_unloaded_result.status.current_state == 'unloaded' + - launchd_unloaded_result.status.current_pid == '-' + +- name: "[{{ item }}] Remove test service configuration" + file: + path: "{{ launchd_plist_location }}" + state: absent + become: true + +- name: "[{{ item }}] Remove test service server" + file: + path: "/usr/local/sbin/ansible_test_service" + state: absent + become: true diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/test.yml new file mode 100644 index 000000000..25a7bba00 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/test.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 + +- name: "Running {{ item }}" + block: + - include_tasks: setup.yml + - include_tasks: "tests/{{ item }}.yml" + always: + - include_tasks: teardown.yml diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_reload.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_reload.yml new file mode 100644 index 000000000..04dc8ae72 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_reload.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: "[{{ item }}] Given a started service in check_mode" + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result_check_mode" + check_mode: true + +- name: "[{{ item }}] Assert that everything work in check mode" + assert: + that: + - test_1_launchd_start_result_check_mode is success + - test_1_launchd_start_result_check_mode is changed + +- name: "[{{ item }}] Given a started service..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result" + + +- name: "[{{ item }}] The started service should run on port 21212" + wait_for: + port: 21212 + delay: 5 + timeout: 10 + +- name: "[{{ item }}] Deploy a new test service configuration with a new port 21213" + template: + src: "modified.{{ launchd_service_name }}.plist.j2" + dest: "{{ launchd_plist_location }}" + become: true + +- name: "[{{ item }}] When reloading the service..." + launchd: + name: "{{ launchd_service_name }}" + state: reloaded + become: true + register: "test_1_launchd_reload_result" + +- name: "[{{ item }}] Validate that service was reloaded" + assert: + that: + - test_1_launchd_reload_result is success + - test_1_launchd_reload_result is changed + - test_1_launchd_reload_result.status.previous_pid == test_1_launchd_start_result.status.current_pid + - test_1_launchd_reload_result.status.previous_state == test_1_launchd_start_result.status.current_state + - test_1_launchd_reload_result.status.current_state == 'stopped' + - test_1_launchd_reload_result.status.current_pid == '-' + +- name: "[{{ item }}] Start the service with the new configuration..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result" + + +- name: "[{{ item }}] The started service should run on port 21213" + wait_for: + port: 21213 + delay: 5 + timeout: 10 diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_restart.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_restart.yml new file mode 100644 index 000000000..44064cef1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_restart.yml @@ -0,0 +1,46 @@ +--- +# 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: "[{{ item }}] Given a started service..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result" + + +- name: "[{{ item }}] When restarting the service in check mode" + launchd: + name: "{{ launchd_service_name }}" + state: restarted + become: true + register: "test_1_launchd_restart_result_check_mode" + check_mode: true + +- name: "[{{ item }}] Validate that service was restarted in check mode" + assert: + that: + - test_1_launchd_restart_result_check_mode is success + - test_1_launchd_restart_result_check_mode is changed + +- name: "[{{ item }}] When restarting the service..." + launchd: + name: "{{ launchd_service_name }}" + state: restarted + become: true + register: "test_1_launchd_restart_result" + +- name: "[{{ item }}] Validate that service was restarted" + assert: + that: + - test_1_launchd_restart_result is success + - test_1_launchd_restart_result is changed + - test_1_launchd_restart_result.status.previous_pid == test_1_launchd_start_result.status.current_pid + - test_1_launchd_restart_result.status.previous_state == test_1_launchd_start_result.status.current_state + - test_1_launchd_restart_result.status.current_state == 'started' + - test_1_launchd_restart_result.status.current_pid != '-' + - test_1_launchd_restart_result.status.status_code == '0' diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_runatload.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_runatload.yml new file mode 100644 index 000000000..87c72d532 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_runatload.yml @@ -0,0 +1,36 @@ +--- +# 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: "[{{ item }}] Given a started service with RunAtLoad set to true..." + launchd: + name: "{{ launchd_service_name }}" + state: started + enabled: true + become: true + register: test_1_launchd_start_result + +- name: "[{{ item }}] Validate that service was started" + assert: + that: + - test_1_launchd_start_result is success + - test_1_launchd_start_result is changed + - test_1_launchd_start_result.status.previous_pid == '-' + - test_1_launchd_start_result.status.previous_state == 'unloaded' + - test_1_launchd_start_result.status.current_state == 'started' + - test_1_launchd_start_result.status.current_pid != '-' + - test_1_launchd_start_result.status.status_code == '0' + +- name: "[{{ item }}] Validate that RunAtLoad is set to true" + replace: + path: "{{ launchd_plist_location }}" + regexp: | + \s+RunAtLoad + \s+ + replace: found_run_at_load + check_mode: true + register: contents_would_have + failed_when: not contents_would_have is changed diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_start_stop.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_start_stop.yml new file mode 100644 index 000000000..bf59979aa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_start_stop.yml @@ -0,0 +1,115 @@ +--- +# 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: "[{{ item }}] Given a started service in check mode" + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result_check_mode" + check_mode: true + + +- name: "[{{ item }}] Validate that service was started in check mode" + assert: + that: + - test_1_launchd_start_result_check_mode is success + - test_1_launchd_start_result_check_mode is changed + + +- name: "[{{ item }}] Given a started service..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result" + + +- name: "[{{ item }}] Validate that service was started" + assert: + that: + - test_1_launchd_start_result is success + - test_1_launchd_start_result is changed + - test_1_launchd_start_result.status.previous_pid == '-' + - test_1_launchd_start_result.status.previous_state == 'unloaded' + - test_1_launchd_start_result.status.current_state == 'started' + - test_1_launchd_start_result.status.current_pid != '-' + - test_1_launchd_start_result.status.status_code == '0' + +# ----------------------------------------------------------- + +- name: "[{{ item }}] Given a stopped service..." + launchd: + name: "{{ launchd_service_name }}" + state: stopped + become: true + register: "test_2_launchd_stop_result" + +- name: "[{{ item }}] Validate that service was stopped after it was started" + assert: + that: + - test_2_launchd_stop_result is success + - test_2_launchd_stop_result is changed + - test_2_launchd_stop_result.status.previous_pid == test_1_launchd_start_result.status.current_pid + - test_2_launchd_stop_result.status.previous_state == test_1_launchd_start_result.status.current_state + - test_2_launchd_stop_result.status.current_state == 'stopped' + - test_2_launchd_stop_result.status.current_pid == '-' + +# ----------------------------------------------------------- + +- name: "[{{ item }}] Given a stopped service..." + launchd: + name: "{{ launchd_service_name }}" + state: stopped + become: true + register: "test_3_launchd_stop_result" + +- name: "[{{ item }}] Validate that service can be stopped after being already stopped" + assert: + that: + - test_3_launchd_stop_result is success + - not test_3_launchd_stop_result is changed + - test_3_launchd_stop_result.status.previous_pid == '-' + - test_3_launchd_stop_result.status.previous_state == 'stopped' + - test_3_launchd_stop_result.status.current_state == 'stopped' + - test_3_launchd_stop_result.status.current_pid == '-' + +# ----------------------------------------------------------- + +- name: "[{{ item }}] Given a started service..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_4_launchd_start_result" + +- name: "[{{ item }}] Validate that service was started..." + assert: + that: + - test_4_launchd_start_result is success + - test_4_launchd_start_result is changed + - test_4_launchd_start_result.status.previous_pid == '-' + - test_4_launchd_start_result.status.previous_state == 'stopped' + - test_4_launchd_start_result.status.current_state == 'started' + - test_4_launchd_start_result.status.current_pid != '-' + - test_4_launchd_start_result.status.status_code == '0' + +- name: "[{{ item }}] And when service is started again..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_5_launchd_start_result" + +- name: "[{{ item }}] Validate that service is still in the same state as before" + assert: + that: + - test_5_launchd_start_result is success + - not test_5_launchd_start_result is changed + - test_5_launchd_start_result.status.previous_pid == test_4_launchd_start_result.status.current_pid + - test_5_launchd_start_result.status.previous_state == test_4_launchd_start_result.status.current_state + - test_5_launchd_start_result.status.status_code == '0' diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unknown.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unknown.yml new file mode 100644 index 000000000..d18ea5453 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unknown.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: "[{{ item }}] Expect that an error occurs when an unknown service is used." + launchd: + name: com.acme.unknownservice + state: started + register: result + failed_when: + - not '"Unable to infer the path of com.acme.unknownservice service plist file and it was not found among active services" in result.msg' diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unload.yml b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unload.yml new file mode 100644 index 000000000..8915aac8b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/tasks/tests/test_unload.yml @@ -0,0 +1,65 @@ +--- +# 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: "[{{ item }}] Given a started service..." + launchd: + name: "{{ launchd_service_name }}" + state: started + become: true + register: "test_1_launchd_start_result" + + +- name: "[{{ item }}] When unloading the service in check mode" + launchd: + name: "{{ launchd_service_name }}" + state: unloaded + become: true + register: "test_1_launchd_unloaded_result_check_mode" + check_mode: true + +- name: "[{{ item }}] Validate that service was unloaded in check mode" + assert: + that: + - test_1_launchd_unloaded_result_check_mode is success + - test_1_launchd_unloaded_result_check_mode is changed + + +- name: "[{{ item }}] When unloading the service..." + launchd: + name: "{{ launchd_service_name }}" + state: unloaded + become: true + register: "test_1_launchd_unloaded_result" + +- name: "[{{ item }}] Validate that service was unloaded" + assert: + that: + - test_1_launchd_unloaded_result is success + - test_1_launchd_unloaded_result is changed + - test_1_launchd_unloaded_result.status.previous_pid == test_1_launchd_start_result.status.current_pid + - test_1_launchd_unloaded_result.status.previous_state == test_1_launchd_start_result.status.current_state + - test_1_launchd_unloaded_result.status.current_state == 'unloaded' + - test_1_launchd_unloaded_result.status.current_pid == '-' + +# ----------------------------------------------------------- + +- name: "[{{ item }}] Given an unloaded service on an unloaded service..." + launchd: + name: "{{ launchd_service_name }}" + state: unloaded + become: true + register: "test_2_launchd_unloaded_result" + +- name: "[{{ item }}] Validate that service did not change and is still unloaded" + assert: + that: + - test_2_launchd_unloaded_result is success + - not test_2_launchd_unloaded_result is changed + - test_2_launchd_unloaded_result.status.previous_pid == '-' + - test_2_launchd_unloaded_result.status.previous_state == 'unloaded' + - test_2_launchd_unloaded_result.status.current_state == 'unloaded' + - test_2_launchd_unloaded_result.status.current_pid == '-' diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/templates/launchd.test.service.plist.j2 b/ansible_collections/community/general/tests/integration/targets/launchd/templates/launchd.test.service.plist.j2 new file mode 100644 index 000000000..43f43c24f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/templates/launchd.test.service.plist.j2 @@ -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 +#} + + + + + Label + {{ launchd_service_name }} + ProgramArguments + + /usr/local/sbin/ansible_test_service + 21212 + + + diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/templates/modified.launchd.test.service.plist.j2 b/ansible_collections/community/general/tests/integration/targets/launchd/templates/modified.launchd.test.service.plist.j2 new file mode 100644 index 000000000..a41b65562 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/templates/modified.launchd.test.service.plist.j2 @@ -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 +#} + + + + + Label + {{ launchd_service_name }} + ProgramArguments + + /usr/local/sbin/ansible_test_service + 21213 + + + diff --git a/ansible_collections/community/general/tests/integration/targets/launchd/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/launchd/vars/main.yml new file mode 100644 index 000000000..ce880ed9d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/launchd/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 + +launchd_service_name: launchd.test.service +launchd_plist_location: /Library/LaunchDaemons/{{ launchd_service_name }}.plist diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/aliases b/ansible_collections/community/general/tests/integration/targets/ldap_search/aliases new file mode 100644 index 000000000..795844548 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/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/1 +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/ldap_search/meta/main.yml new file mode 100644 index 000000000..d282aa0dc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/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_openldap diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/main.yml new file mode 100644 index 000000000..521075b5e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/main.yml @@ -0,0 +1,16 @@ +--- +#################################################################### +# 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: Run LDAP search module tests + block: + - include_tasks: "{{ item }}" + with_fileglob: + - 'tests/*.yml' + when: ansible_os_family in ['Ubuntu', 'Debian'] 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 new file mode 100644 index 000000000..36d245d39 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.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/basic.yml + +#################################################################### +## Search ########################################################## +#################################################################### +- name: Test simple search for a user + ldap_search: + dn: "ou=users,dc=example,dc=com" + scope: "onelevel" + filter: "(uid=ldaptest)" + ignore_errors: true + register: output + +- name: assert that test LDAP user can be found + assert: + that: + - output is not failed + - output.results | length == 1 + - output.results.0.displayName == "LDAP Test" diff --git a/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/aliases b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/aliases new file mode 100644 index 000000000..620afe071 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/aliases @@ -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 + +azp/posix/3 +destructive +skip/aix +skip/osx +skip/macos +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/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/listen_ports_facts/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml new file mode 100644 index 000000000..70649f505 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml @@ -0,0 +1,112 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Test playbook for the listen_ports_facts module +# Copyright (c) 2019, Nathan Davison + +# 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 netstat and netcat on deb + ansible.builtin.package: + name: + - net-tools + - netcat + state: latest + when: ansible_os_family == "Debian" + +- name: install netstat and netcat on rh < 7 + ansible.builtin.package: + name: + - net-tools + - nc.x86_64 + state: latest + when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int < 7 + +- name: install netcat on rh >= 7 + ansible.builtin.package: + name: 'nmap-ncat' + state: latest + when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7 + +- name: start UDP server on port 5555 + command: nc -u -l -p 5555 + async: 1000 + poll: 0 + when: (ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7) or ansible_os_family == "Debian" + +- name: start UDP server on port 5555 + command: nc -u -l 5555 + async: 1000 + poll: 0 + when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int < 7 + +- name: start TCP server on port 5556 + command: "nc -l -p 5556" + async: 1000 + poll: 0 + when: (ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7) or ansible_os_family == "Debian" + +- name: start TCP server on port 5556 + command: "nc -l 5556" + async: 1000 + poll: 0 + when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int < 7 + +- name: Gather listening ports facts + listen_ports_facts: + when: ansible_os_family == "RedHat" or ansible_os_family == "Debian" + +- name: check that the include_non_listening parameters ('state' and 'foreign_address') are not active in default setting + assert: + that: + - ansible_facts.tcp_listen | selectattr('state', 'defined') | list | length == 0 + - ansible_facts.tcp_listen | selectattr('foreign_address', 'defined') | list | length == 0 + when: ansible_os_family == "RedHat" or ansible_os_family == "Debian" + +- name: Gather listening ports facts explicitly via netstat and include_non_listening + listen_ports_facts: + command: 'netstat' + include_non_listening: 'yes' + when: (ansible_os_family == "RedHat" and ansible_distribution_major_version|int < 7) or ansible_os_family == "Debian" + +- name: Gather listening ports facts explicitly via ss and include_non_listening + listen_ports_facts: + command: 'ss' + include_non_listening: 'yes' + when: ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7 + +- name: check for ansible_facts.udp_listen exists + assert: + that: ansible_facts.udp_listen is defined + when: ansible_os_family == "RedHat" or ansible_os_family == "Debian" + +- name: check for ansible_facts.tcp_listen exists + assert: + that: ansible_facts.tcp_listen is defined + when: ansible_os_family == "RedHat" or ansible_os_family == "Debian" + +- name: check that the include_non_listening parameter 'state' and 'foreign_address' exists + assert: + that: + - ansible_facts.tcp_listen | selectattr('state', 'defined') | list | length > 0 + - ansible_facts.tcp_listen | selectattr('foreign_address', 'defined') | list | length > 0 + when: ansible_os_family == "RedHat" or ansible_os_family == "Debian" + +- name: check TCP 5556 is in listening ports + assert: + that: 5556 in ansible_facts.tcp_listen | map(attribute='port') | sort | list + when: (ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7) or ansible_os_family == "Debian" + +- name: check UDP 5555 is in listening ports + assert: + that: 5555 in ansible_facts.udp_listen | map(attribute='port') | sort | list + when: (ansible_os_family == "RedHat" and ansible_distribution_major_version|int >= 7) or ansible_os_family == "Debian" + +- name: kill all async commands + command: "kill -9 {{ item.pid }}" + loop: "{{ [tcp_listen, udp_listen]|flatten }}" + when: item.name == 'nc' + ignore_errors: true diff --git a/ansible_collections/community/general/tests/integration/targets/locale_gen/aliases b/ansible_collections/community/general/tests/integration/targets/locale_gen/aliases new file mode 100644 index 000000000..f7f4063f6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/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/3 +destructive +needs/root +skip/aix 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 new file mode 100644 index 000000000..c6bdcc046 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/locale_gen.yml @@ -0,0 +1,99 @@ +--- +# 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 new file mode 100644 index 000000000..de3e673be --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml @@ -0,0 +1,12 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2014, James Tanner +# 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') diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases new file mode 100644 index 000000000..2bdcc0113 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/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 +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_cartesian/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/tasks/main.yml new file mode 100644 index 000000000..5575f22ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/tasks/main.yml @@ -0,0 +1,32 @@ +--- +#################################################################### +# 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: Test cartesian lookup + debug: var=item + register: product + with_community.general.cartesian: + - - A + - B + - C + - - '1' + - '2' + - '3' +- name: Verify cartesian lookup + assert: + that: + - product.results[0]['item'] == ["A", "1"] + - product.results[1]['item'] == ["A", "2"] + - product.results[2]['item'] == ["A", "3"] + - product.results[3]['item'] == ["B", "1"] + - product.results[4]['item'] == ["B", "2"] + - product.results[5]['item'] == ["B", "3"] + - product.results[6]['item'] == ["C", "1"] + - product.results[7]['item'] == ["C", "2"] + - product.results[8]['item'] == ["C", "3"] diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/aliases new file mode 100644 index 000000000..343f119da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/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/3 diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/galaxy.yml b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/galaxy.yml new file mode 100644 index 000000000..2243e0dba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/galaxy.yml @@ -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 + +namespace: testns +name: testcoll +version: 0.0.1 +authors: + - Ansible (https://github.com/ansible) +description: null +tags: [community] diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py new file mode 100644 index 000000000..e7f1a987a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: collection_module +short_description: Test collection module +description: + - This is a test module in a local collection. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json new file mode 100644 index 000000000..57bc66cc2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json @@ -0,0 +1,40 @@ +{ + "files": [ + { + "name": ".", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "README.md", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "format": 1 + }, + { + "name": "plugins", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules", + "ftype": "dir", + "chksum_type": null, + "chksum_sha256": null, + "format": 1 + }, + { + "name": "plugins/modules/collection_module.py", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "c3f0114d080c409c58c8846be8da7b91137b38eaf2d24f72a4a61a303f925f4d", + "format": 1 + } + ], + "format": 1 +} diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json.license b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/FILES.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json new file mode 100644 index 000000000..e4a9e7d8f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json @@ -0,0 +1,30 @@ +{ + "collection_info": { + "namespace": "testns", + "name": "testcoll_mf", + "version": "0.0.1", + "authors": [ + "Ansible (https://github.com/ansible)" + ], + "readme": "README.md", + "tags": [ + "community" + ], + "description": null, + "license": [], + "license_file": null, + "dependencies": {}, + "repository": null, + "documentation": null, + "homepage": null, + "issues": null + }, + "file_manifest_file": { + "name": "FILES.json", + "ftype": "file", + "chksum_type": "sha256", + "chksum_sha256": "025818f18fcae5c9f78d778ae6e246ecffed6d56a886ffbc145cb66d54e9951e", + "format": 1 + }, + "format": 1 +} diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json.license b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/MANIFEST.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/README.md b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/plugins/modules/collection_module.py b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/plugins/modules/collection_module.py new file mode 100644 index 000000000..e7f1a987a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_mf/plugins/modules/collection_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: collection_module +short_description: Test collection module +description: + - This is a test module in a local collection. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nothing/plugins/modules/collection_module.py b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nothing/plugins/modules/collection_module.py new file mode 100644 index 000000000..e7f1a987a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nothing/plugins/modules/collection_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: collection_module +short_description: Test collection module +description: + - This is a test module in a local collection. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/galaxy.yml b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/galaxy.yml new file mode 100644 index 000000000..96aae3d64 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/galaxy.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 + +namespace: testns +name: testcoll_nv +authors: + - Ansible (https://github.com/ansible) +description: null +tags: [community] diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/plugins/modules/collection_module.py b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/plugins/modules/collection_module.py new file mode 100644 index 000000000..e7f1a987a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/collections/ansible_collections/testns/testcoll_nv/plugins/modules/collection_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: collection_module +short_description: Test collection module +description: + - This is a test module in a local collection. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/library/local_module.py b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/library/local_module.py new file mode 100644 index 000000000..9e9e649cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/library/local_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: local_module +short_description: Test local module +description: + - This is a test module locally next to a playbook. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.sh new file mode 100755 index 000000000..118abbc29 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.sh @@ -0,0 +1,20 @@ +#!/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 + +# The collection loader ignores paths which have more than one ansible_collections in it. +# That's why we have to copy this directory to a temporary place and run the test there. + +# Create temporary folder +TEMPDIR=$(mktemp -d) +trap '{ rm -rf ${TEMPDIR}; }' EXIT + +cp -r . "${TEMPDIR}" +cd "${TEMPDIR}" + +ansible-playbook runme.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.yml b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.yml new file mode 100644 index 000000000..54c58614f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_collection_version/runme.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 + +- hosts: localhost + tasks: + - name: Test collection_version + assert: + that: + # Collection that does not exist + - query('community.general.collection_version', 'foo.bar') == [none] + - lookup('community.general.collection_version', 'foo.bar', result_not_found='foo') == 'foo' + # Collection that exists + - lookup('community.general.collection_version', 'community.general') is string + # Local collection + - lookup('community.general.collection_version', 'testns.testcoll') == '0.0.1' + # Local collection with no version + - lookup('community.general.collection_version', 'testns.testcoll_nv') == '*' + - lookup('community.general.collection_version', 'testns.testcoll_nv', result_no_version='') == '' + # Local collection with MANIFEST.json + - lookup('community.general.collection_version', 'testns.testcoll_mf') == '0.0.1' + # Local collection with no galaxy.yml and no MANIFEST.json + - lookup('community.general.collection_version', 'testns.testcoll_nothing') == '*' + - lookup('community.general.collection_version', 'testns.testcoll_nothing', result_no_version='0.0.0') == '0.0.0' + # Multiple collection names at once + - lookup('community.general.collection_version', 'testns.testcoll', 'testns.testcoll_nv', 'testns.testcoll_nv', 'testns.testcoll_mf', 'foo.bar') + == ['0.0.1', '*', '*', '0.0.1', none] + + - name: Invalid FQCN + set_fact: + test: "{{ query('community.general.collection_version', 'foo.bar.baz') }}" + ignore_errors: true + register: invalid_fqcn + + - name: Validate error message + assert: + that: + - > + '"foo.bar.baz" is not a FQCN' in invalid_fqcn.msg diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases new file mode 100644 index 000000000..26ad5c244 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_dependent/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/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_dependent/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_dependent/tasks/main.yml new file mode 100644 index 000000000..b2f209729 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_dependent/tasks/main.yml @@ -0,0 +1,183 @@ +--- +# 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 1 + set_fact: + loop_result: >- + {{ + query('community.general.dependent', + dict(key1=[1, 2]), + dict(key2='[item.key1 + 3, item.key1 + 6]'), + dict(key3='[item.key1 + item.key2 * 10]')) + }} + +- name: Check result of Test 1 + assert: + that: + - loop_result == expected_result + vars: + expected_result: + - key1: 1 + key2: 4 + key3: 41 + - key1: 1 + key2: 7 + key3: 71 + - key1: 2 + key2: 5 + key3: 52 + - key1: 2 + key2: 8 + key3: 82 + +- name: Test 2 + set_fact: + loop_result: >- + {{ query('community.general.dependent', + dict([['a', [1, 2, 3]]]), + dict([['b', '[1, 2, 3, 4] if item.a == 1 else [2, 3, 4] if item.a == 2 else [3, 4]']])) }} + # The last expression could have been `range(item.a, 5)`, but that's not supported by all Jinja2 versions used in CI + +- name: Check result of Test 2 + assert: + that: + - loop_result == expected_result + vars: + expected_result: + - a: 1 + b: 1 + - a: 1 + b: 2 + - a: 1 + b: 3 + - a: 1 + b: 4 + - a: 2 + b: 2 + - a: 2 + b: 3 + - a: 2 + b: 4 + - a: 3 + b: 3 + - a: 3 + b: 4 + +- name: Test 3 + debug: + var: item + with_community.general.dependent: + - var1: + a: + - 1 + - 2 + b: + - 3 + - 4 + - var2: 'item.var1.value' + - var3: 'dependent_lookup_test[item.var1.key ~ "_" ~ item.var2]' + loop_control: + label: "{{ [item.var1.key, item.var2, item.var3] }}" + register: dependent + vars: + dependent_lookup_test: + a_1: + - A + - B + a_2: + - C + b_3: + - D + b_4: + - E + - F + - G + +- name: Check result of Test 3 + assert: + that: + - (dependent.results | length) == 7 + - dependent.results[0].item.var1.key == "a" + - dependent.results[0].item.var2 == 1 + - dependent.results[0].item.var3 == "A" + - dependent.results[1].item.var1.key == "a" + - dependent.results[1].item.var2 == 1 + - dependent.results[1].item.var3 == "B" + - dependent.results[2].item.var1.key == "a" + - dependent.results[2].item.var2 == 2 + - dependent.results[2].item.var3 == "C" + - dependent.results[3].item.var1.key == "b" + - dependent.results[3].item.var2 == 3 + - dependent.results[3].item.var3 == "D" + - dependent.results[4].item.var1.key == "b" + - dependent.results[4].item.var2 == 4 + - dependent.results[4].item.var3 == "E" + - dependent.results[5].item.var1.key == "b" + - dependent.results[5].item.var2 == 4 + - dependent.results[5].item.var3 == "F" + - dependent.results[6].item.var1.key == "b" + - dependent.results[6].item.var2 == 4 + - dependent.results[6].item.var3 == "G" + +- name: "Test 4: template failure" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - a: + - 1 + - 2 + - b: "[item.a + foo]" + ignore_errors: true + register: eval_error + +- name: Check result of Test 4 + assert: + that: + - eval_error is failed + - eval_error.msg.startswith("Caught \"'foo' is undefined") + +- name: "Test 5: same variable name reused" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - a: x + - b: x + ignore_errors: true + register: eval_error + +- name: Check result of Test 5 + assert: + that: + - eval_error is failed + - eval_error.msg.startswith("Caught \"'x' is undefined") + +- name: "Test 6: multi-value dict" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - a: x + b: x + ignore_errors: true + register: eval_error + +- name: Check result of Test 6 + assert: + that: + - eval_error is failed + - eval_error.msg == 'Parameter 0 must be a one-element dictionary, got 2 elements' + +- name: "Test 7: empty dict" + debug: + msg: "{{ item }}" + with_community.general.dependent: + - {} + ignore_errors: true + register: eval_error + +- name: Check result of Test 7 + assert: + that: + - eval_error is failed + - eval_error.msg == 'Parameter 0 must be a one-element dictionary, got 0 elements' diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases new file mode 100644 index 000000000..eb449a9cf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_dig/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/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_dig/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_dig/meta/main.yml new file mode 100644 index 000000000..fe9e33681 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_dig/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_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_dig/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_dig/tasks/main.yml new file mode 100644 index 000000000..2f48333cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_dig/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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 dnspython library + pip: + name: dnspython + state: present + extra_args: "-c {{ remote_constraints }}" + +- name: Test dig lookup with existing domain + set_fact: + dig_existing: "{{ lookup('community.general.dig', 'github.com.') }}" + +- name: Test dig lookup with non-existing domain and fail_on_error=no + set_fact: + dig_nonexisting_fail_no: "{{ lookup('community.general.dig', 'non-existing.domain.', 'fail_on_error=no') }}" + +- name: Verify that NXDOMAIN was returned + assert: + that: dig_nonexisting_fail_no == 'NXDOMAIN' + +- name: Test dig lookup with non-existing domain and fail_on_error=yes + set_fact: + dig_nonexisting_fail_yes: "{{ lookup('community.general.dig', 'non-existing.domain.', 'fail_on_error=yes') }}" + ignore_errors: true + register: dig_nonexisting_fail_yes_result + +- name: Verify that the task failed + assert: + that: dig_nonexisting_fail_yes_result is failed diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases new file mode 100644 index 000000000..b9f3395f7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases @@ -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 + +azp/posix/1 +destructive +needs/file/tests/utils/constraints.txt +needs/target/setup_etcd3 +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_etcd3/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/defaults/main.yml new file mode 100644 index 000000000..de726382b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/defaults/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 + + etcd3_prefix: '/keyprefix/' + etcd3_singlekey: '/singlekeypath' diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/dependencies.yml b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/dependencies.yml new file mode 100644 index 000000000..ea012594d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/dependencies.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 + +- hosts: localhost + tasks: + - name: Setup etcd3 + import_role: + name: setup_etcd3 diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/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/lookup_etcd3/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/runme.sh new file mode 100755 index 000000000..1b37ae4f3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/runme.sh @@ -0,0 +1,11 @@ +#!/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 + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook dependencies.yml -v "$@" + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test_lookup_etcd3.yml -v "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/main.yml new file mode 100644 index 000000000..47f1916c0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/main.yml @@ -0,0 +1,28 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# lookup_etcd3 integration tests +# Copyright 2020, SCC France, Eric Belhomme +# 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: put key/values with an etcd prefix + etcd3: + key: "{{ etcd3_prefix }}foo{{ item }}" + value: "bar{{ item }}" + state: present + loop: + - 1 + - 2 + - 3 + +- name: put a single key/values in etcd + etcd3: + key: "{{ etcd3_singlekey }}" + value: "foobar" + state: present + +- import_tasks: tests.yml diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/tests.yml b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/tests.yml new file mode 100644 index 000000000..929c6f142 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/tasks/tests.yml @@ -0,0 +1,27 @@ +--- +# lookup_etcd3 integration tests +# Copyright 2020, SCC France, Eric Belhomme +# 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: 'Fetch secrets using "etcd3" lookup' + set_fact: + etcdoutkey1: "{{ lookup('community.general.etcd3', etcd3_prefix, prefix=True) }}" + etcdoutkey2: "{{ lookup('community.general.etcd3', etcd3_singlekey) }}" + key_inexistent: "{{ lookup('community.general.etcd3', 'inexistent_key') }}" + + - name: 'Check etcd values' + assert: + msg: 'unexpected etcd3 values' + that: + - etcdoutkey1 is sequence + - etcdoutkey1 | length() == 3 + - etcdoutkey1[0].value == 'bar1' + - etcdoutkey1[1].value == 'bar2' + - etcdoutkey1[2].value == 'bar3' + - etcdoutkey2 is sequence + - etcdoutkey2 | length() == 2 + - etcdoutkey2.value == 'foobar' + - key_inexistent is sequence + - key_inexistent | length() == 0 diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/test_lookup_etcd3.yml b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/test_lookup_etcd3.yml new file mode 100644 index 000000000..c18138888 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/test_lookup_etcd3.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 + +- hosts: localhost + tasks: + - name: Test lookup etcd3 + import_role: + name: lookup_etcd3 diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases new file mode 100644 index 000000000..0ac9bad98 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_flattened/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 +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_flattened/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_flattened/tasks/main.yml new file mode 100644 index 000000000..37af1327b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_flattened/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: test with_flattened + set_fact: '{{ item }}=flattened' + with_community.general.flattened: + - - a__ + - - b__ + - - c__ + - d__ +- name: verify with_flattened results + assert: + that: + - a__ == 'flattened' + - b__ == 'flattened' + - c__ == 'flattened' + - d__ == 'flattened' 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 new file mode 100644 index 000000000..66632fb4a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/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/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/dependencies.yml b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/dependencies.yml new file mode 100644 index 000000000..9fc63b19f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/dependencies.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 + +- hosts: localhost + tasks: + - name: Install LMDB Python package + pip: + name: lmdb + environment: + LMDB_PURE: "1" + - name: Setup test data + script: test_db.py + args: + executable: "{{ ansible_python.executable }}" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/runme.sh new file mode 100755 index 000000000..71faa439d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/runme.sh @@ -0,0 +1,11 @@ +#!/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 + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook dependencies.yml -v "$@" + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test.yml -v "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test.yml b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test.yml new file mode 100644 index 000000000..217c020ca --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test.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 + +- hosts: localhost + tasks: + - debug: + msg: '{{ query("community.general.lmdb_kv", "nl", "be", "lu", db="jp.mdb") }}' + - debug: + var: item.1 + loop: '{{ query("community.general.lmdb_kv", db="jp.mdb") }}' + - assert: + that: + - query('community.general.lmdb_kv', 'nl', 'be', 'lu', db='jp.mdb') == ['Netherlands', 'Belgium', 'Luxembourg'] + - query('community.general.lmdb_kv', db='jp.mdb')|length == 5 + - assert: + that: + - item.0 == 'nl' + - item.1 == 'Netherlands' + vars: + - lmdb_kv_db: jp.mdb + with_community.general.lmdb_kv: + - n* + - assert: + that: + - item == 'Belgium' + vars: + - lmdb_kv_db: jp.mdb + with_community.general.lmdb_kv: + - be diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test_db.py b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test_db.py new file mode 100644 index 000000000..b906c4c39 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/test_db.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +import lmdb +map_size = 1024 * 100 +env = lmdb.open('./jp.mdb', map_size=map_size) +with env.begin(write=True) as txn: + txn.put('fr'.encode(), 'France'.encode()) + txn.put('nl'.encode(), 'Netherlands'.encode()) + txn.put('es'.encode(), 'Spain'.encode()) + txn.put('be'.encode(), 'Belgium'.encode()) + txn.put('lu'.encode(), 'Luxembourg'.encode()) 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 new file mode 100644 index 000000000..eb449a9cf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/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/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 new file mode 100755 index 000000000..52a38f4a5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# 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 +set -eux + +ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \ + ansible-playbook test.yml "$@" + +ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \ +ANSIBLE_MERGE_VARIABLES_PATTERN_TYPE=suffix \ + ansible-playbook test_with_env.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test.yml new file mode 100644 index 000000000..fbd884393 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test.yml @@ -0,0 +1,174 @@ +--- +# 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 + hosts: localhost + tasks: + - name: Include test data + include_vars: vars.yml + + # Test the default behavior + - name: Test merge list + block: + - name: Print the merged list + debug: + msg: "{{ merged_list }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list | length) == 2" + - "'item1' in merged_list" + - "'item3' in merged_list" + vars: + merged_list: "{{ lookup('community.general.merge_variables', '^.+__merge_list$') }}" + + - name: Test merge dict + block: + - name: Print the merged list + debug: + msg: "{{ merged_dict }}" + + - name: Validate that dict is complete + assert: + that: + - "'item1' in merged_dict" + - "'item2' in merged_dict" + - "'list_item' in merged_dict" + - "(merged_dict.list_item | length) == 2" + - "'test1' in (merged_dict.list_item)" + - "'test2' in (merged_dict.list_item)" + vars: + merged_dict: "{{ lookup('community.general.merge_variables', '^.+__merge_dict$') }}" + + # Test the behavior when no results are found + - name: Test merge without results + block: + - debug: + msg: "{{ not_found }}" + - name: Validate that the variable defaults to an empty list + vars: + assert: + that: + - "(not_found | default('default-used', True)) == 'default-used'" + vars: + not_found: "{{ lookup('community.general.merge_variables', '^.+__merge_not_found$') }}" + + # Test the 'pattern_type' options + - name: Test merge list (pattern_type = prefix) + block: + - name: Print the merged list + debug: + msg: "{{ merged_list }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list | length) == 4" + - "'item1' in merged_list" + - "'item2' in merged_list" + - "'item2' in merged_list" + - "'item3' in merged_list" + vars: + merged_list: "{{ lookup('community.general.merge_variables', 'testlist', pattern_type='prefix') }}" + + - name: Test merge list (pattern_type = suffix) + block: + - name: Print the merged list + debug: + msg: "{{ merged_list }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list | length) == 2" + - "'item1' in merged_list" + - "'item3' in merged_list" + vars: + merged_list: "{{ lookup('community.general.merge_variables', '__merge_list', pattern_type='suffix') }}" + + - name: Test merge list (pattern_type = regex) + block: + - name: Print the merged list + debug: + msg: "{{ merged_list }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list | length) == 3" + - "'item1' in merged_list" + - "'item2' in merged_list" + - "'item3' in merged_list" + vars: + merged_list: "{{ lookup('community.general.merge_variables', '^testlist[0-9].*', pattern_type='regex') }}" + + # Test the 'initial_value' option + - name: Test merge without results but with initial value + block: + - name: Print the merged list + debug: + msg: "{{ not_found_initial_value }}" + + - name: Validate that the variable only contains the initial value + vars: + assert: + that: + - "(not_found_initial_value | count) == 1" + - "(not_found_initial_value | first) == 'item2'" + vars: + not_found_initial_value: "{{ lookup('community.general.merge_variables', '^.+__merge_not_found$', initial_value=testlist_initial_value) }}" + + - name: Test merging a list with an initial value + block: + - name: Print the merged list + debug: + msg: "{{ merged_list_with_initial_value }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list_with_initial_value | length) == 3" + - "'item1' in merged_list_with_initial_value" + - "'item2' in merged_list_with_initial_value" + - "'item3' in merged_list_with_initial_value" + vars: + merged_list_with_initial_value: "{{ lookup('community.general.merge_variables', '^.+__merge_list$', initial_value=testlist_initial_value) }}" + + # Test the 'override' options + - name: Test the 'override=warn' option + block: + - name: Print the merged list + debug: + msg: "{{ merged_with_override_warn }}" + + - name: Validate that the dict is complete and the warning is printed + assert: + that: + - "'key_to_override' in merged_with_override_warn" + - "merged_with_override_warn.key_to_override == 'Override value'" + - "'key_to_override' in lookup('file', logging_output_file)" # Check if a message is given + - "'[WARNING]' in lookup('file', logging_output_file)" # and verify that the message is a WARNING + vars: + merged_with_override_warn: "{{ lookup('community.general.merge_variables', '^.+__override_warn$', initial_value=override_warn_init, override='warn') }}" + + - name: Test the 'override=error' option + block: + - name: Validate that an override result in an error + debug: + msg: "{{ lookup('community.general.merge_variables', '^.+__override_error$', initial_value=override_error_init, override='error') }}" + ignore_errors: true # Do not stop the playbook + register: _override_error_result + + - name: Print the output + debug: + msg: "{{ _override_error_result }}" + + - name: Validate that the error is reported + assert: + that: + - "_override_error_result.failed" + - "'key_to_override' in _override_error_result.msg" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_with_env.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_with_env.yml new file mode 100644 index 000000000..7fbb664fd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_with_env.yml @@ -0,0 +1,44 @@ +--- +# 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 + hosts: localhost + tasks: + - name: Include test data + include_vars: vars.yml + + # Test the pattern option using the environment variable + - name: Test merge list (pattern_type = regex) + block: + - name: Print the merged list + debug: + msg: "{{ merged_list }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list | length) == 2" + - "'item1' in merged_list" + - "'item3' in merged_list" + vars: + merged_list: "{{ lookup('community.general.merge_variables', '__merge_list') }}" + + # Test whether the pattern option can be overridden + - name: Test merge list (pattern_type = suffix) + block: + - name: Print the merged list + debug: + msg: "{{ merged_list }}" + + - name: Validate that the list is complete + assert: + that: + - "(merged_list | length) == 3" + - "'item1' in merged_list" + - "'item2' in merged_list" + - "'item3' in merged_list" + vars: + merged_list: "{{ lookup('community.general.merge_variables', '^testlist[0-9].*', pattern_type='regex') }}" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/vars.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/vars.yml new file mode 100644 index 000000000..d1a4ace21 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/vars.yml @@ -0,0 +1,34 @@ +--- +# 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 + +testlist_initial_value: "{{ testlist2 }}" +testlist1__merge_list: + - item1 +testlist2: + - item2 +testlist3__merge_list: + - item3 + +testdict1__merge_dict: + item1: test + list_item: + - test1 +testdict2__merge_dict: + item2: test + list_item: + - test2 + +override_warn_init: + key_to_override: Initial value +override__override_warn: + key_to_override: Override value + +override_error_init: + key_to_override: Initial value +override__override_error: + key_to_override: Override value + +logging_output_file: /tmp/ansible-test-merge-variables # The Ansible log output is available in this file diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases new file mode 100644 index 000000000..0d4c5af3b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/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/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_passwordstore/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/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/lookup_passwordstore/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/main.yml new file mode 100644 index 000000000..c0b5eb5bd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/main.yml @@ -0,0 +1,17 @@ +--- +#################################################################### +# 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: + - include_tasks: package.yml + - include_tasks: tests.yml + when: + # The pass package is no longer available in EPEL, so only test on Fedora, OpenSUSE, FreeBSD, macOS, and Ubuntu + # https://lists.zx2c4.com/pipermail/password-store/2019-July/003689.html + - ansible_facts.distribution in ['FreeBSD', 'MacOSX', 'openSUSE Leap', 'Ubuntu'] diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/package.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/package.yml new file mode 100644 index 000000000..e5ccd5677 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/package.yml @@ -0,0 +1,84 @@ +--- +# 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: Include distribution specific variables + include_vars: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - "{{ ansible_facts.distribution }}.yml" + - "{{ ansible_facts.os_family }}.yml" + - default.yml + paths: + - "{{ role_path }}/vars" + +- name: Install package + action: "{{ ansible_facts.pkg_mgr }}" + args: + name: "{{ passwordstore_packages }}" + state: present + when: ansible_facts.pkg_mgr in ['apt', 'dnf', 'yum', 'pkgng', 'community.general.pkgng'] + +- block: + # OpenSUSE Leap>=15.0 don't include password-store in main repo + - name: SUSE | Add security:privacy repo + template: + src: security-privacy.repo.j2 + dest: /etc/zypp/repos.d/security:privacy.repo + + - name: SUSE | Install package + package: + name: password-store + state: present + update_cache: true + disable_gpg_check: true + when: ansible_facts.pkg_mgr in ['zypper', 'community.general.zypper'] + +# See https://github.com/gopasspw/gopass/issues/1849#issuecomment-802789285 +- name: Install gopass on Debian + when: ansible_facts.os_family == 'Debian' + become: true + block: + - name: Fetch gopass repo keyring + ansible.builtin.get_url: + url: https://packages.gopass.pw/repos/gopass/gopass-archive-keyring.gpg + dest: /usr/share/keyrings/gopass-archive-keyring.gpg + - name: Add gopass repo + ansible.builtin.apt_repository: + repo: "deb [arch=amd64,arm64,armhf \ + signed-by=/usr/share/keyrings/gopass-archive-keyring.gpg] \ + https://packages.gopass.pw/repos/gopass stable main" + state: present + - name: Update apt-cache and install gopass package + ansible.builtin.apt: + name: gopass + update_cache: true + +- name: Install on macOS + when: ansible_facts.distribution == 'MacOSX' + block: + - name: MACOS | Find brew binary + command: which brew + register: brew_which + + - name: MACOS | Get owner of brew binary + stat: + path: "{{ brew_which.stdout }}" + register: brew_stat + + - name: MACOS | Install package + homebrew: + name: + - gnupg2 + - pass + - gopass + state: present + update_homebrew: false + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + # Newer versions of brew want to compile a package which takes a long time. Do not upgrade homebrew until a + # proper solution can be found + environment: + HOMEBREW_NO_AUTO_UPDATE: "True" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml new file mode 100644 index 000000000..a94529e46 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/password_tests.yml @@ -0,0 +1,130 @@ +--- +# 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 password ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass', length=8, create=true, backend=backend) }}" + + - name: Fetch password from an existing file ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-pass', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Create a password with equal sign ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal userpass=SimpleSample= create=true', backend=backend) }}" + + - name: Fetch a password with equal sign ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Create a password using missing=create ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create', missing='create', length=8, backend=backend) }}" + + - name: Fetch password from an existing file ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Fetch password from existing file using missing=empty ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create', missing='empty', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Fetch password from non-existing file using missing=empty ({{ backend }}) + set_fact: + readpass: "{{ query('community.general.passwordstore', 'test-missing-pass', missing='empty', backend=backend) }}" + + - name: Verify password ({{ backend }}) + assert: + that: + - readpass == [ none ] + + - name: Create the YAML password ({{ backend }}) + command: "{{ backend }} insert -m -f test-yaml-pass" + args: + stdin: | + testpassword + key: | + multi + line + + - name: Fetch a password with YAML subkey ({{ backend }}) + set_fact: + readyamlpass: "{{ lookup('community.general.passwordstore', 'test-yaml-pass', subkey='key', backend=backend) }}" + + - name: Read a yaml subkey ({{ backend }}) + assert: + that: + - readyamlpass == 'multi\nline\n' + + - name: Create a non-YAML multiline file ({{ backend }}) + command: "{{ backend }} insert -m -f test-multiline-pass" + args: + stdin: | + testpassword + random additional line + + - name: Fetch password from multiline file ({{ backend }}) + set_fact: + readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass', backend=backend) }}" + + - name: Multiline pass only returns first line ({{ backend }}) + assert: + that: + - readyamlpass == 'testpassword' + + - name: Fetch all from multiline file ({{ backend }}) + set_fact: + readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass', returnall='yes', backend=backend) }}" + + - name: Multiline pass returnall returns everything in the file ({{ backend }}) + assert: + that: + - readyamlpass == 'testpassword\nrandom additional line\n' + + - name: Create a password in a folder ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass', length=8, create=true, backend=backend) }}" + + - name: Fetch password from folder ({{ backend }}) + set_fact: + readpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass', backend=backend) }}" + + - name: Verify password from folder ({{ backend }}) + assert: + that: + - readpass == newpass + + - name: Try to read folder as passname ({{ backend }}) + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'folder', backend=backend) }}" + ignore_errors: true + register: eval_error + + - name: Make sure reading folder as passname failed ({{ backend }}) + assert: + that: + - eval_error is failed + - '"passname folder not found" in eval_error.msg' + when: backend != "gopass" # Remove this line once gopass backend can handle this diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/tests.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/tests.yml new file mode 100644 index 000000000..65a578c96 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/tasks/tests.yml @@ -0,0 +1,239 @@ +--- +# 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: Check name of gpg2 binary + command: which gpg2 + register: gpg2_check + ignore_errors: true + +- name: Set gpg2 binary name + set_fact: + gpg2_bin: '{{ "gpg2" if gpg2_check is successful else "gpg" }}' + +- name: Stop gpg-agent so we can remove any locks on the GnuPG dir + command: gpgconf --kill gpg-agent + ignore_errors: true + +- name: Remove previous password files and directory + file: + dest: "{{ item }}" + state: absent + loop: + - "~/.gnupg" + - "~/.password-store" + +- name: Get path of pass executable + command: which pass + register: result + +- name: Store path of pass executable + set_fact: + passpath: "{{ result.stdout }}" + +- name: Move original pass into place if there was a leftover + command: + argv: + - mv + - "{{ passpath }}.testorig" + - "{{ passpath }}" + args: + removes: "{{ passpath }}.testorig" + +# having gopass is not required for this test, but we store +# its path in case it is installed, so we can restore it +- name: Try to find gopass in path + command: which gopass + register: result + +- name: Store path of gopass executable + set_fact: + gopasspath: "{{ result.stdout }}" + +- name: Move original gopass into place if there was a leftover + command: + argv: + - mv + - "{{ gopasspath }}.testorig" + - "{{ gopasspath }}" + args: + removes: "{{ gopasspath }}.testorig" + +- name: Get versions of tools + command: "{{ item }} --version" + register: versions + loop: + - "{{ gpg2_bin }}" + - pass + - gopass + +- name: Output versions of tools + debug: + msg: "{{ versions.results | map(attribute='stdout_lines') }}" + +# How to generate a new GPG key: +# gpg2 --batch --gen-key input # See templates/input +# gpg2 --list-secret-keys --keyid-format LONG +# gpg2 --armor --export-secret-keys [key id] +# # Get the fingerprint +# gpg2 --fingerprint --keyid-format LONG | grep [key id] -A 1 | tail -1 | tr -d '[:space:]' | awk -F '=' '{print $2":6:"}' + +- name: Import GPG private key + shell: echo "{{ passwordstore_privkey }}" | {{ gpg2_bin }} --import --allow-secret-key-import - + +- name: Trust key + shell: echo "D3E1CC8934E97270CEB066023AF1BD3619AB496A:6:" | {{ gpg2_bin }} --import-ownertrust + +- name: Initialise pass passwordstore + command: pass init ansible-test + +- name: Initialise gopass passwordstore + command: gopass init --path $HOME/.gopass-store ansible-test + args: + creates: "{{ lookup('env','HOME') }}/.gopass-store" + +# these tests should apply to all backends +- name: Password tests + include_tasks: password_tests.yml + loop: + - pass + - gopass + loop_control: + loop_var: backend + +- name: Change passwordstore location explicitly + set_fact: + passwordstore: "{{ lookup('env','HOME') }}/.password-store" + +- name: Make sure password store still works with explicit location set + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" + +- name: Change passwordstore location to a non-existent place + set_fact: + passwordstore: "somenonexistentplace" + +- name: Try reading from non-existent passwordstore location + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" + ignore_errors: true + register: eval_error + +- name: Make sure reading from non-existent passwordstore location failed + assert: + that: + - eval_error is failed + - >- + "Passwordstore directory '" in eval_error.msg + - >- + "/somenonexistentplace' does not exist" in eval_error.msg + +- name: Test pass compatibility shim detection + block: + - name: Move original pass out of the way + command: + argv: + - mv + - "{{ passpath }}" + - "{{ passpath }}.testorig" + args: + creates: "{{ passpath }}.testorig" + + - name: Create dummy pass script + ansible.builtin.copy: + content: | + #!/bin/sh + echo "shim_ok" + dest: "{{ passpath }}" + mode: '0755' + + - name: Try reading from non-existent passwordstore location with different pass utility + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" + environment: + PATH: "/tmp" + + - name: Verify password received from shim + assert: + that: + - newpass == "shim_ok" + + - name: Try to read folder as passname with a different pass utility + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" + + - name: Verify password received from shim + assert: + that: + - newpass == "shim_ok" + + always: + - name: Move original pass back into place + command: + argv: + - mv + - "{{ passpath }}.testorig" + - "{{ passpath }}" + args: + removes: "{{ passpath }}.testorig" + +# This are in addition to the real gopass tests above +# and verify plugin logic +- name: gopass plugin logic tests + vars: + passwordstore_backend: "gopass" + block: + - name: Check if gopass executable exists + stat: + path: "{{ gopasspath }}" + register: gopass_check + + - name: Move original gopass out of the way + command: + argv: + - mv + - "{{ gopasspath }}" + - "{{ gopasspath }}.testorig" + args: + creates: "{{ gopasspath }}.testorig" + when: gopass_check.stat.exists == true + + - name: Create mocked gopass script + ansible.builtin.copy: + content: | + #!/bin/sh + if [ "$GOPASS_NO_REMINDER" != "YES" ]; then + exit 1 + fi + if [ "$1" = "--version" ]; then + exit 2 + fi + echo "gopass_ok" + dest: "{{ gopasspath }}" + mode: '0755' + + - name: Try to read folder as passname using gopass mock + set_fact: + newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" + + - name: Verify password received from gopass mock + assert: + that: + - newpass == "gopass_ok" + + always: + - name: Remove mocked gopass + ansible.builtin.file: + path: "{{ gopasspath }}" + state: absent + + - name: Move original gopass back into place + command: + argv: + - mv + - "{{ gopasspath }}.testorig" + - "{{ gopasspath }}" + args: + removes: "{{ gopasspath }}.testorig" + when: gopass_check.stat.exists == true diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input new file mode 100644 index 000000000..d639accdb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input @@ -0,0 +1,9 @@ +%echo Generating a Ansible Test PGP key +Key-Type: RSA +Key-Length: 4096 +Subkey-Type: RSA +Subkey-Length: 4096 +Name-Real: ansible-test +Expire-Date: 0 +%commit +%echo done diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input.license b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/input.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/security-privacy.repo.j2 b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/security-privacy.repo.j2 new file mode 100644 index 000000000..72eca99ec --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/templates/security-privacy.repo.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 +#} + +[security_privacy] +name=Crypto applications and utilities (openSUSE_Leap_{{ ansible_distribution_version }}) +type=rpm-md +baseurl=http://download.opensuse.org/repositories/security:/privacy/openSUSE_Leap_{{ ansible_distribution_version }}/ +gpgcheck=1 +gpgkey=http://download.opensuse.org/repositories/security:/privacy/openSUSE_Leap_{{ ansible_distribution_version }}/repodata/repomd.xml.key +enabled=1 diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Alpine.yml new file mode 100644 index 000000000..f18329ed1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Alpine.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 + +passwordstore_packages: + - gopass + - pass diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Archlinux.yml new file mode 100644 index 000000000..f18329ed1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Archlinux.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 + +passwordstore_packages: + - gopass + - pass diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Debian.yml new file mode 100644 index 000000000..825a6a8bb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Debian.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 + +passwordstore_packages: + - pass diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Fedora.yml new file mode 100644 index 000000000..f18329ed1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/Fedora.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 + +passwordstore_packages: + - gopass + - pass diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.yml new file mode 100644 index 000000000..9e9da2772 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/FreeBSD.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 + +passwordstore_packages: + - gopass + - gnupg + - password-store diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/default.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/default.yml @@ -0,0 +1,4 @@ +--- +# 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/lookup_passwordstore/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/main.yml new file mode 100644 index 000000000..2b4fa1b22 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/vars/main.yml @@ -0,0 +1,122 @@ +--- +# 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 + +passwordstore_privkey: | + -----BEGIN PGP PRIVATE KEY BLOCK----- + Version: GnuPG v2.0.22 (GNU/Linux) + + lQcYBF0L9gUBEACrYsKIj/rXFQPURHz+YKg54BW6utIvwoF/CbQarc8iXoXfPZW4 + wQnFaX+dLifkvX5f4xIUVD94qyMXT2oNg+HZXH2y7VwqBFeG9TrNqfgJsYTbTlgP + 0MOD3FtZkMy/6TrJyOzY7x7oHUwWY1S5YeBDBsRqWwhS5fIfHLGbZLwoHC/53mlP + ve0zL1u28y3Kh8JvYBSlcYrbMlbKQb1g0TSb8gVwq+Iv2CriEmHipUvLI02z02Ny + XnT0+rzOEEynaoF7zX1S0eoIwKz46/sen1L5YAYhLM16gq4WSquxUz8klff+jVD5 + uLgUKzEkhBdBuTvWKjknk6Wu2aDPC6IQXUjm+5dIh+/IyD4SKPA+HY1QZjIF/6nH + KV43RRzB8YWkAEW/iQdKCXWyNz1o8zYiun/PxiJtgV2qpz4z5M+ANx1VCj31eMTE + A0exSnMLjbUknE3JX2bnBfTgbiKbeXQcL6FNrKIPJrwgNlP3g48JzY6aPT3Tej8Q + pd6DqamU0sQfoi2K8zs/Ltl+REocKqzQ9cz0dj7ykQqAMf+TunQfC3pY8yI7IKCr + YM2L3aJbiqLTp31dCfwxBSZJ+oalUzp2l91ugu1k1mjuik7ZJaflfBNEFWK9ig3v + qhv8FwdoPFNh8TC11btfU1wphcBl65pCaPff2s94/Gj2aQ0PrFYuhPtzqwARAQAB + AA//UMXH1yK8XrfJyTXKDv0w663fr9SykM3DyEKjfltiHtmbkF3u/VcFKuQQv9xr + 8tMYB0r2T1xxWYaWkDreSaZZP97mYviZEjhfo/xZjWpIuxDOA6nFuRZzvuaQqwKi + bOQXz9gBQDBaSZzdKkQAPyqQziYXVeS3ZJJ47Q7R6eGtB95ZAhM/YNSrQQ9V00CC + 2UvoaCNJN7vubGYqH0KiZUnT2JdU1wg7Hr9rXoa5WV77/K4Txeefm9xGlNrDNv7Z + kaGRiu6K3QiPmzZrjxlwjbsbGOqXmPULlmyWbW0dxAHu5WhEq9SgUEEtiFve2k3i + xBfvyny12SAt2t04e7wI0luUqnpQR8j3JPFXcCiFszIauMbXhIAd0QhRfS6/pRCf + iBRliFqvQpgmz9w8/enfQtBvyAwZLr3p2vk4OyRW/GLFnfkoKcCvgZtL5vNDPYJm + Y1koC+jAsiiFRmUvP9SLyNabVTANz5Hg/jZmcdh+OTWs1xdZl1JyjUOxV6n1n1pW + BPc0FaNvFS+wkx6Rp9DgryTP1oTD6wjacNljFh3A9LJ0DTnQgsghoi5ArBKnRP7R + 9i0DKUqywoukm+GQHoZlB6bglDBVc3wKZvtw17/SgD6GnKZ3zH+Y8yx3K3MI9wjT + Od1jMxQxzKWMxrv72mtzchm/utkubL5BpM5hn6fg32NEkxEIAMhc2f01fuv/UZ1i + zlkqXkcMzrd/+9+Mv53meLMJsW2biOwRF52ZXi3k9ulUwHB21FaAXeyOFhueKrh/ + iKu5Hpydxruj0XCgMRArgvghPL4KLfhh54xvXGKxWw7B0IWkOnvELPikOl3h17cY + lQ5rN5mQtlxaqqrJKOxkseEFTvVJudZXZH9oArlVXO88HklDeEHtV4xjdiyvtFKg + qWUvo6oNT0LmpFdgstoKJ8H5gKiV3wfl2QJQxqWT40wUFVnNEAoBYC7PbHWajxmD + 7ZGoKE9o3ythg11D4rH23uTUFLd5Hc5xeQ2/+OhEKv4Qe0X+okv8/RpM94ObfsW9 + HdQBsgMIANr6B/SzwhPn8eK0c6wrOYU/B/V370qgTBRpWgrNRCvtN8NuSJKOJRU/ + qYm74dCsVxBfvUlEPRp9DEsE9djvwZLdIqOfn4amDoLZmYdMQ5LQCOaHlrnEx+ng + uHUklFUXIHDNcVRWzhrHm4KQWeB7RrCRL1nEimW/nhh8y++4TmxZQ1Zr2fdUWdMs + dSWryw3RE5nwDd7nW8+Wgm3TfS4jhhn3DcKFZxLzG1eo4ZaXoPa4j7zps3xFyBtF + KMPgrvAAxzqFwklQKjwXcthYUQ5OzXTt94m8VqOz0nZGoizaGBFRz1l1q9QQxTv4 + BUI+2OeyfrzWIaKEu+9gsNbx/OfxmzkH/2quLKJj0FQ+aEYkeGXVtf2DsceQXB1l + QtBo/qvBWP2XYk6GzAfwjvI8ifEG4MzXCZxm5SKtQ8UljrCo2T6OArG2FK1FSJDX + UlQBXPLYWIJCkC9X8SB6UztPSXPoNS6Ktc0K5XFxvGzIDpxAE+zE4UAe3gWGaROe + bwxbuRb9/aHAEk6CU3mrgEUUqet+4qUNRpAzJdwYIN6nOAVghHWiVY4OoCSCgMYY + 4B9Aa9bVeDQH9S88X5ux3bDW1DPjiewNIYo+0z53gfY3HZeDfHFWD4iM6vaJjSVb + 65trGHyGhElkWub56Q3nHXPOxiAKeRBn3iL54wDNiGMlfw/tkoAkTuaNI7QMYW5z + aWJsZS10ZXN0iQI5BBMBAgAjBQJdC/YFAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwEC + HgECF4AACgkQOvG9NhmrSWo7YhAAhsp+j13R3bJkv/esJwaDa0dbdm1iJzKB9bkf + Bi10qMFmJUEDyHHKL9OBiAgSCKna5F4EuEFnaV9YPs1n6PVE+FX3m5UAfCULR6Qk + G064+nd25LWEjSJ3I85WHfJNz/fPr3fQvQNH67GEdTZIr7hrWeZHH1nnpGrZ6xx6 + sVBxKMp3g8wNXey/DJSaDcry5cQ8cZW2RrUzmfXgcSlwAIVBkmHKA1UtgAGu1kq/ + LzaCJus7ffMdUZd7IYAM5fIdnNEf0fi8/oKcWiv9TqynGJmu2AxjSUu9EG9idESu + bZtXZntM2ODbqepfQ0i44ew9Y3HQS8ciP8uhbQYFZuhbMQobXNIkvO6XA1cePSt2 + Wh4qCh+ymk9u2qBqb4JkqORyOCJhLdOj6TGU0o9TQ8h0EqjB/Wx69ppA0GFQh5si + CG7GnwZhabgiPIxoCPQuhDPv+rXgFx5YiGofMddst9DFn0nR/fFx9hqaTuZ4iiuH + UzvqQAMGtIMxiOdJKSSI9emsNfQvXTMHjB+s6Cjiw7nF0+G2ciXlLTPbtTFePZVN + seDosuN6uMqwm8KNZVJpU0O0qXu5EdI7ptbOooSR7WZSJdQ+MWyr0vGYKKFGYkwp + jl/pDzXCA1d3AxK4Mzmb+KvFQvh+9X7VwI9Pgg4HHE5KeyX8wXhrvT2itPoznnC2 + 33tCCZmdBxcEXQv2BQEQANtkIv93lunfgRUt7X4hJXT8rw/Y787b+xQ/FcApQnfd + 5Zg6pubrMPbOoIKFJG4vzNBSmXGSBmqGIdIYqT2eR9eBDoOv5Cl8tCQ+zNoC2V0Q + uCOLZV86hoakduHniCv8cKSbsG6mm5oFP61/82yJLlPUarT+EGSuWCR6W1pGC5WR + GElnE9VFpaQ5TZ8A3EBWky2YhdX7vOzbjP8x0Jd/3UFfpNd5gRnxfJLx8rrdKt20 + xYxR4FPUbu9kQFZIyUr2kxNi30R1+oK4hcXbID6gqxt1oW5PWGkNOXYTY6r/Vv6D + zU4Bf4gngWc7hgwbtGRkv2jR8Zv3ZIUbo4ZwMAMMs3Un7RWjjEJkrtUzdaIdjtwM + jZIH7xMNz/NK748EB3uMKiIOkmWqHrWkU2aa86V8heuTg/AWluKFG6J+WHzzYnPE + pb+WbWbZi2PcIQlDY2EDQyluXN0YKdHeFRXdo5QllN+oZ54e0EVioYzpUlzyD4/m + sqfGS/ZF//r7EoTeIbrqBJDbEc9pjB3cphpWWHLxxbo42u27w+Gvda6B+1ad2bZX + lBp8tnQl2y5JtMPWW7kVZs5EBPS8OY5NRWqVAFPBg1OlnC6OYC2c1rW7tqZll0T0 + UORR+zdhayYDtLZhJdD5QcSVLRe26jlyeT4920dOUvjI8ANiRSjSOx3wwcnnhtLt + ABEBAAEAD/jW435kO/7VlNYaqKG2qUDHFbljDFnXhCCp9CCZ19ADGJWKRei0xilv + lXQiY8cLJka2xjEFzMH8FOWLpBwg/qffrclJsz8IY90Oo3SDFcdSIM48Ao2IeQrL + Vswa+W2np9utX9GwkABZHEsC5hDIfpWiBq1+Glx0QVCUakSQZ4txNG1VeGE0xMM5 + 1+bvlygk3KfMQVjV/31Ngr7YNzLZMaTGI6iSZbDOeHpMdDAMWBVkk2vrxUa01Z7T + XJ6n5SNFCb+FfZKyu9xjrdlZswgiT71JaC52ncE7JHjj7pnxI6lSIkc14MHJ2ugk + 9WiW84v9ybCyOvEsk2Uh+7BwPfLJCee7SIWcVses55mVUm0YNoU68isQfuuuF2+B + OwTaoFT5sDwGlE7exdgk7DyUYxIIB3aRTUfNYeAIVW2uR5GruOgTLUw54KPa1N7O + NAfiC4OAfc+s6KBTU/Reozdq6mewLEM0UBccEmBtWevet64p5TWleyaL1TtEPZlX + DnrkTXA/ZRTCteuSLwmMDpcFEYc3IcgZIQfvqtHO2cDJ67AjlsgvEDwTV65l1MnN + krYIgUh8yFMnFGZPO1jw3mRtuU0QottdPj14Xcn855GS2C4CZ31N3YJq7eR+j5Bh + SmXk6k5ys8eF/gw4rbEaBbXFTjw8eb1q7Nwus0W+0yrq4r9/J1fxCADkCFXD95jm + sddOHz0Dih6c3KEvpkiPLzITOSp5/nPyd3K7T3xx0L2sBQQt2QGT0zeJCwAB5uTE + uTb6AjKcvOn7KlPQQqP9O2mgHo7Zzwr/I69DkYLuLGNCsr0XGi5sn9aaDG0dpFqg + 2VzItHVTCefoVF5EOmOaDwB1QYunKwXy3vtgNBJ7LwJb4rm1TgNHsheT/rtUsVYP + Z7lAxcLGa2xNm215nf8fzmbjZnCfPEyRpMgqlN+YUzGiDJwfN/fwr8OgFnjDSqOL + htbqCiLv7vTbsw6TWIuKr21QVEc3Qcqu96v0dNXk6wr4PTnPmezswSK0+Dc2+5JZ + PBkYIkbE5eYxCAD2THuc0iiqX4ZFS8KhbjWvmng08xulj45BpHCkY8oRcaMCRD0c + AoEDZyPEfwfb8SMNb2NHzvYi8djM0uU5NzqURKUF22bAAbxkgPyBKp8CBECD0X50 + O4HpJIYShbfUD/XWKLvxGxKYlrOvwHuUboPPnMqsaMLwezu0MoaUztbTlr/OZgA0 + 8/hwiyXDcsrf7yg5BPfAZghC1OYEvhEiS43VefAPVv/bvwz3Aml0kQLH14eZxGxL + z7mb0qoZJ0Qkn36QQ2L9GcWYRfBDgUXcxfZm6JW+UZWuWMBAPMSaPoRd9yt+MMPC + QsmyUrBxT9vTLFysUIJYQThycucvO9Xud/19CADt0G5p1u8uRzKi5Qnbtd79V0v5 + Obud6LRooXJNprYVLhPE6lr0aeeZK6QDIrmRrZqeByQ2kfY6XP6Tl4/9wZX2ajS/ + 8GJGmv7HP6vqfJdrOQhnAjQkUYYm72C/eicAsm8e/fiOSrpf1ithyPKOArFqVyth + pVkAnteqNDABBNellUbqS//XUKnk/Cd2WWaGe4J/nIuj78MphBEzSO4CmWkdbH5G + CPyXdq6LEnDp1By3LNmZTwqbeMUtzVcaJVh6k4jW3OEL9h0PIWQ8Ol42+0Wn57Pn + 5vPzGwKaf7zFhJlLRCiCdJJl4QOjXo0jvQHBvelQ8NVpeCCAabWmR9OwldqOhJiJ + BD4EGAECAAkFAl0L9gUCGy4CKQkQOvG9NhmrSWrBXSAEGQECAAYFAl0L9gUACgkQ + kjr8nlUnTh1fBBAAqFbBzvU44nvoxYtBz0b3ZJ8TBCu8rCAwXEquvb+sXWYj52jh + ThW+ke24rX178JZDPu6f8EZwF98voZCDxU5XTexqMugTubVSMmTGbJ63YN99Nl5P + jjdwO4r1LaBJ7ef30YLT0ZIUI73dDTolQ0ENHxwtD1xnwx8JvXpmQdDJ9/HINQlU + HRSt2qWHRSgrutRLFlO7trWQZXIrUwjY3qgKJMPYc2tJqmfsoD6EeISyZOVOJ7m5 + xgs1f7UgtbVrHYhQOxRiMIAbMDbukRKwlvp1af8R7e+EoFMIcewaObe6/PUTuOFL + 0VkCWoHBBKWAJQJ7vHmzW1tgyrDjchHSUAGMqZOEL84uOCWqMQ/6HCj/zaEqiOqg + fuoh54K05lKE5OOIBWITVGgqsT9tli29Lov9vJb2p4csN4kSrdKJpLCgP21V8Utk + ZWR1OgDhD7h40Eobpph4KauYoAZiAfu3cb4BzNhUAJ69fJ5lrOlKP1GLmYyQ5jfx + s73TDCNfj42OBeUCO6tncTSPXs/9P2FziynVLxkCT8cbVq1C4H87BO7TEW9FuxKJ + hLfpVGbt/yG1HrvGJ/kRPk0sXu2md9quWkh6qPHF5EThCOrlfbwLD5Pqvt0ZPZlR + xxMSRP9L9w09ZYO1Y7f6gegElTpEh/aFLq1jjUxm8h/cO6A9lJ3Bjxb/xmoKoBAA + ieGiHu3YgsN0jmxvlnf7GwB89BNajyH6D0tbdsH+OeSU8e6xItxpe6s5GfonWAt+ + ngPutPSPhgS5AUx8GrxHGu3Sx+wmGvGKpsH+2Tu1ciUN34K/sfrzKjxCuvnpcBTd + rOSiEObnKnb6OI6sW329ZH4z/r5tVoetWr45xspc9IE8TVIuavOlJkuInX5/B3Sa + DPwAH/aQAYc71j7yDr7ezFzx07h+pH4ePeGsnfdKy6ZWoQ1mmM35j93ZhH8P8YCC + N8lklRhxvZhXkHWt4ns/QzT+QawW2sR8Kkha3ydzx9cEcalmNq7nG+QkwSlliazE + 6peVL6ga2H1+XM+1p/P/qCsvbLmRobWSyfMURKkL05iykNjnHOYno+A+NaM3IR12 + uf5tWvNfiJpNXbUf8Yjh0ep73aZfF1kODIcLR1AHtVVt0Yc05XKEwFfv4kADi1Mh + Pp0s4MHRMjPCgPU1j2b5ulGZGZKlCECu3799bw8yb7n9Hpj42hL0ZOEsMdMHbCfd + 7eQUNNncVW/KDnwrJQAabr/771xSTauWDDdpEJEc2Mdx3m6e7doQvboYaKYvK4kg + x/Vi7pcHS1xQO0zC8BPPBq9tMyy5QGSybfVSvMPo+7nIsumZ9fvJwjjRHreZP4pa + nbE9Gt4CrEbWoo6jbcscycpQbduEhGtvwj8UFXe5z+M= + =o0Ig + -----END PGP PRIVATE KEY BLOCK----- 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 new file mode 100644 index 000000000..0ac9bad98 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/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 +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_pet/dependencies.yml b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/dependencies.yml new file mode 100644 index 000000000..464b5c428 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/dependencies.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 + +- hosts: localhost + tasks: + - name: Install Petname Python package + pip: + name: petname diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/runme.sh new file mode 100755 index 000000000..71faa439d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/runme.sh @@ -0,0 +1,11 @@ +#!/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 + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook dependencies.yml -v "$@" + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test.yml -v "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/test.yml b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/test.yml new file mode 100644 index 000000000..c61461867 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/test.yml @@ -0,0 +1,30 @@ +--- +# 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 + gather_facts: false + tasks: + - name: Call plugin + set_fact: + result1: "{{ query('community.general.random_pet', words=3) }}" + result2: "{{ query('community.general.random_pet', length=3) }}" + result3: "{{ query('community.general.random_pet', prefix='kubernetes') }}" + result4: "{{ query('community.general.random_pet', separator='_') }}" + result5: "{{ query('community.general.random_pet', words=2, length=6, prefix='kubernetes', separator='_') }}" + + - name: Check results + assert: + that: + - result1 | length == 1 + - result1[0].split('-') | length == 3 + - result2 | length == 1 + - result2[0].split('-')[0] | length <= 3 + - result3 | length == 1 + - result3[0].split('-')[0] == 'kubernetes' + - result4 | length == 1 + - result4[0].split('_') | length == 2 + - result5 | length == 1 + - result5[0].split('_') | length == 3 + - result5[0].split('_')[0] == 'kubernetes' 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 new file mode 100644 index 000000000..0ac9bad98 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/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 +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/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/runme.sh new file mode 100755 index 000000000..35c79500c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/runme.sh @@ -0,0 +1,8 @@ +#!/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 + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test.yml -v "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_string/test.yml b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/test.yml new file mode 100644 index 000000000..b1f623410 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/test.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 + +- hosts: localhost + gather_facts: false + tasks: + - name: Call plugin + set_fact: + result1: "{{ query('community.general.random_string') }}" + result2: "{{ query('community.general.random_string', length=0) }}" + result3: "{{ query('community.general.random_string', length=10) }}" + result4: "{{ query('community.general.random_string', length=-1) }}" + result5: "{{ query('community.general.random_string', override_special='_', min_special=1) }}" + result6: "{{ query('community.general.random_string', upper=false, special=false) }}" # lower case only + result7: "{{ query('community.general.random_string', lower=false, special=false) }}" # upper case only + result8: "{{ query('community.general.random_string', lower=false, upper=false, special=false) }}" # number only + result9: "{{ query('community.general.random_string', lower=false, upper=false, special=false, min_numeric=1, length=1) }}" # single digit only + result10: "{{ query('community.general.random_string', numbers=false, upper=false, special=false, min_lower=1, length=1) }}" # single lowercase character only + result11: "{{ query('community.general.random_string', base64=true, length=8) }}" + result12: "{{ query('community.general.random_string', upper=false, numbers=false, special=false) }}" # all lower case + result13: "{{ query('community.general.random_string', override_all='0', length=2) }}" + + - name: Raise error when impossible constraints are provided + set_fact: + impossible: "{{ query('community.general.random_string', upper=false, lower=false, special=false, numbers=false) }}" + ignore_errors: true + register: impossible_result + + - name: Check results + assert: + that: + - result1[0] | length == 8 + - result2[0] | length == 0 + - result3[0] | length == 10 + - result4[0] | length == 0 + - result5[0] | length == 8 + - "'_' in result5[0]" + - result6[0] is lower + - result7[0] is upper + - result8[0] | regex_replace('^(\d+)$', '') == '' + - result9[0] | regex_replace('^(\d+)$', '') == '' + - result9[0] | length == 1 + - result10[0] | length == 1 + - result10[0] is lower + # if input string is not multiple of 3, base64 encoded string will be padded with = + - result11[0].endswith('=') + - result12[0] is lower + - result13[0] | length == 2 + - result13[0] == '00' + - impossible_result is failed + - "'Available characters cannot' in impossible_result.msg" 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 new file mode 100644 index 000000000..0ac9bad98 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/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 +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/dependencies.yml b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/dependencies.yml new file mode 100644 index 000000000..1cb0b0d3a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/dependencies.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 + +- hosts: localhost + tasks: + - name: Install xkcdpass Python package + pip: + name: xkcdpass diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_words/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/runme.sh new file mode 100755 index 000000000..71faa439d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/runme.sh @@ -0,0 +1,11 @@ +#!/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 + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook dependencies.yml -v "$@" + +ANSIBLE_ROLES_PATH=../ \ + ansible-playbook test.yml -v "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_words/test.yml b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/test.yml new file mode 100644 index 000000000..90c672730 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/test.yml @@ -0,0 +1,32 @@ +--- +# 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 + gather_facts: false + tasks: + - name: Call random_words plugin + set_fact: + result1: "{{ query('community.general.random_words') }}" + result2: "{{ query('community.general.random_words', min_length=5, max_length=5) }}" + result3: "{{ query('community.general.random_words', delimiter='!') }}" + result4: "{{ query('community.general.random_words', numwords=3, delimiter='-', case='capitalize') }}" + result5: "{{ query('community.general.random_words', min_length=5, max_length=5, numwords=3, delimiter='') }}" + + - name: Check results + assert: + that: + - result1 | length == 1 + - result1[0] | length >= 35 + - result2 | length == 1 + - result2[0] | length == 35 + - result3 | length == 1 + - result3[0].count("!") == 5 + - result4 | length == 1 + - result4[0] | length >= 17 + - result4[0] | length <= 29 + - result4[0] | regex_findall("[A-Z]") | length == 3 + - result4[0].count("-") == 2 + - result5 | length == 1 + - result5[0] | length == 15 diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/aliases b/ansible_collections/community/general/tests/integration/targets/lvg/aliases new file mode 100644 index 000000000..3b92ba75c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/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/lvg/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/lvg/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/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/lvg/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml new file mode 100644 index 000000000..e14c48c3f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/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 # +#################################################################### + +# 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 lvg module + block: + - import_tasks: setup.yml + + - import_tasks: test_indempotency.yml + + - import_tasks: test_grow_reduce.yml + + - import_tasks: test_pvresize.yml + always: + - import_tasks: teardown.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 new file mode 100644 index 000000000..3984b9fc3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml @@ -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 + +- 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' + +- 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: "Affect name on disk to work on" + set_fact: + loop_device1: "{{ loop_device1.stdout }}" + loop_device2: "{{ loop_device2.stdout }}" 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 new file mode 100644 index 000000000..de4957321 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.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 test volume group + lvg: + vg: testvg + state: absent + +- name: Detach loop devices + command: "losetup -d {{ item }}" + loop: + - "{{ loop_device1 | default('') }}" + - "{{ loop_device2 | default('') }}" + when: + - item != '' + +- name: Remove device files + file: + path: "{{ remote_tmp_dir }}/img{{ item }}" + state: absent + with_sequence: 'count=2' diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_grow_reduce.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_grow_reduce.yml new file mode 100644 index 000000000..857df9246 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_grow_reduce.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 + +- name: "Create volume group on first disk" + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + +- name: "get lvm facts" + setup: + +- debug: var=ansible_lvm + +- name: "Assert the testvg span only on first disk" + assert: + that: + - ansible_lvm.pvs[loop_device1].vg == "testvg" + - 'loop_device2 not in ansible_lvm.pvs or + ansible_lvm.pvs[loop_device2].vg == ""' + +- name: "Extend to second disk AND reduce from the first disk" + lvg: + vg: testvg + pvs: "{{ loop_device2 }}" + +- name: "get lvm facts" + setup: + +- debug: var=ansible_lvm + +- name: "Assert the testvg span only on first disk" + assert: + that: + - 'loop_device1 not in ansible_lvm.pvs or + ansible_lvm.pvs[loop_device1].vg == ""' + - ansible_lvm.pvs[loop_device2].vg == "testvg" diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_indempotency.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_indempotency.yml new file mode 100644 index 000000000..758912484 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_indempotency.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 + +- name: Create volume group on disk device + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + +- name: Create the volume group again to verify idempotence + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + register: repeat_vg_create + +- name: Do all assertions to verify expected results + assert: + that: + - repeat_vg_create is not changed 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 new file mode 100644 index 000000000..f15add91c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.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 + +- name: "Create volume group on first disk" + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + +- name: Gets current vg size + shell: vgs -v testvg -o pv_size --noheading --units b | xargs + register: cmd_result + +- name: Assert the testvg size is 8388608B + assert: + that: + - "'8388608B' == 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" + +- name: "Reread size of file associated with loop_device1" + command: "losetup -c {{ loop_device1 }}" + +- name: "Reruns lvg with pvresize:no" + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + pvresize: false + register: cmd_result + +- assert: + that: + - cmd_result is not changed + +- name: Gets current vg size + shell: vgs -v testvg -o pv_size --noheading --units b | xargs + register: cmd_result + +- name: Assert the testvg size is still 8388608B + assert: + that: + - "'8388608B' == cmd_result.stdout" + +- name: "Reruns lvg with pvresize:yes and check_mode:yes" + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + pvresize: true + check_mode: true + register: cmd_result + +- name: Assert that the module returned the state was changed + assert: + that: + - cmd_result is changed + +- name: Gets current vg size + shell: vgs -v testvg -o pv_size --noheading --units b | xargs + register: cmd_result + +- name: Assert the testvg size is still 8388608B + assert: + that: + - "'8388608B' == cmd_result.stdout" + +- name: "Reruns lvg with pvresize:yes" + lvg: + vg: testvg + pvs: "{{ loop_device1 }}" + pvresize: true + +- name: Gets current vg size + shell: vgs -v testvg -o pv_size --noheading --units b | xargs + register: cmd_result + +- name: Assert the testvg size is now 16777216B + assert: + that: + - "'16777216B' == cmd_result.stdout" diff --git a/ansible_collections/community/general/tests/integration/targets/lxd_project/aliases b/ansible_collections/community/general/tests/integration/targets/lxd_project/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lxd_project/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/lxd_project/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lxd_project/tasks/main.yml new file mode 100644 index 000000000..d1340eebd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lxd_project/tasks/main.yml @@ -0,0 +1,142 @@ +#################################################################### +# 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: Clean up test project + lxd_project: + name: ansible-test-project + state: absent + +- name: Clean up test project + lxd_project: + name: ansible-test-project-renamed + state: absent + +- name: Create test project + lxd_project: + name: ansible-test-project + config: + features.images: "false" + features.networks: "true" + features.profiles: "true" + limits.cpu: "3" + state: present + register: results + +- name: Check project has been created correctly + assert: + that: + - results is changed + - results.actions is defined + - "'create' in results.actions" + +- name: Create test project again with merge_project set to true + lxd_project: + name: ansible-test-project + merge_project: true + config: + features.images: "false" + features.networks: "true" + features.profiles: "true" + limits.cpu: "3" + state: present + register: results + +- name: Check state is not changed + assert: + that: + - results is not changed + - "{{ results.actions | length }} == 0" + +- name: Create test project again with merge_project set to false + lxd_project: + name: ansible-test-project + merge_project: false + config: + features.images: "false" + features.networks: "true" + features.profiles: "true" + limits.cpu: "3" + state: present + register: results + +- name: Check state is not changed + assert: + that: + - results is changed + - "'apply_projects_configs' in results.actions" + +- name: Update project test => update description + lxd_project: + name: ansible-test-project + merge_project: false + description: "ansible test project" + config: + features.images: "false" + features.networks: "true" + features.profiles: "true" + limits.cpu: "3" + state: present + register: results + +- name: Check state is changed + assert: + that: + - results is changed + - "'apply_projects_configs' in results.actions" + +- name: Update project test => update project config + lxd_project: + name: ansible-test-project + merge_project: false + description: "ansible test project" + config: + features.images: "false" + features.networks: "true" + features.profiles: "true" + limits.cpu: "4" + state: present + register: results + +- name: Check state is changed + assert: + that: + - results is changed + - "'apply_projects_configs' in results.actions" + +- name: Rename project test + lxd_project: + name: ansible-test-project + new_name: ansible-test-project-renamed + merge_project: true + description: "ansible test project" + config: + features.images: "false" + features.networks: "true" + features.profiles: "true" + limits.cpu: "4" + state: present + register: results + +- name: Check state is changed + assert: + that: + - results is changed + - "'rename' in results.actions" + +- name: Clean up test project + lxd_project: + name: ansible-test-project-renamed + state: absent + register: results + +- name: Check project is deleted + assert: + that: + - results is changed + - "'delete' in results.actions" diff --git a/ansible_collections/community/general/tests/integration/targets/mail/aliases b/ansible_collections/community/general/tests/integration/targets/mail/aliases new file mode 100644 index 000000000..afda346c4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mail/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/1 diff --git a/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.crt b/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.crt new file mode 100644 index 000000000..2fcbb3a1d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.crt @@ -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 + +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIJAJyHQUcqSOQpMA0GCSqGSIb3DQEBCwUAMG4xCzAJBgNV +BAYTAkJFMRMwEQYDVQQIDApWbGFhbmRlcmVuMQ0wCwYDVQQHDARHZW50MQ4wDAYD +VQQKDAVEYWdpdDELMAkGA1UECwwCSVQxHjAcBgNVBAMMFWxvY2FsaG9zdC5sb2Nh +bGRvbWFpbjAeFw0xODExMjgxMjQ3MzlaFw0yODExMjUxMjQ3MzlaMG4xCzAJBgNV +BAYTAkJFMRMwEQYDVQQIDApWbGFhbmRlcmVuMQ0wCwYDVQQHDARHZW50MQ4wDAYD +VQQKDAVEYWdpdDELMAkGA1UECwwCSVQxHjAcBgNVBAMMFWxvY2FsaG9zdC5sb2Nh +bGRvbWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANLqBGgIF44U +zRhNupGwSKAeTIXT4nXPIJKlIi1kTSQwtywQmBw6leBlvj1qwU73+nhqwSclIrYx +3ltvrpKHAWG1jqqsExuLRaKRdWgx1YC2WPgZwYC0C+LkE8vs/Kl1v0HgPuPMkzeK +hDctQfWOaykFOy0mB/BfP2vSVoEckffMlDjG/bHwNt7cG8BnqKd8e9VR+ZcBazFK +bnKhht0ldR84Wbp+5wpuCr1R1R0ltdO2O+LACrXzvH9Kf0CGhKXGccwGpi43eXyK +CDbubkGcLjg9Fo7kZ6uW5nU2vHJ1iDGnvUl8X96qKoOFU0EvBveCisc1bY433uG1 +NjEZ1xLPGK8CAwEAAaNQME4wHQYDVR0OBBYEFO6nDFzJBZBLJt4yza+VrUEOy3Zl +MB8GA1UdIwQYMBaAFO6nDFzJBZBLJt4yza+VrUEOy3ZlMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBALTq0ycKhEr/3KOsfKBup4bs5Oqv0x7ePaUNxyef +JSyKTjD0gPY8YNAeNA7gU5XGjMr4h9cNpRmJ0TyfwWJxH4uK4d2p5k1ZpQWKv8jG +4U9sZTQzkh8nqRBaEl94qsiCIRCllb6VveWbIGE6eqt4rT0V9l9fvbw+hSXdiYXT +KkkX5VZxctV2OMkbP1mbOYIA22jqZKQiIvAVcMA6vSnlDAJKTi9/kw99/zjUQ9Jb +8bF2gcnzAijJAWsCqf8hZVq9+pogptBd/bkKUCuTA4MACX5ppgQltkgX2mLrj6Ep +Po2euqzUZREzKl2cUaP85m+8tClYk0Wjfm0RjxPRa8fgUfM= +-----END CERTIFICATE----- diff --git a/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.key b/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.key new file mode 100644 index 000000000..193ec9cda --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.key @@ -0,0 +1,32 @@ +# 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----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDS6gRoCBeOFM0Y +TbqRsEigHkyF0+J1zyCSpSItZE0kMLcsEJgcOpXgZb49asFO9/p4asEnJSK2Md5b +b66ShwFhtY6qrBMbi0WikXVoMdWAtlj4GcGAtAvi5BPL7Pypdb9B4D7jzJM3ioQ3 +LUH1jmspBTstJgfwXz9r0laBHJH3zJQ4xv2x8Dbe3BvAZ6infHvVUfmXAWsxSm5y +oYbdJXUfOFm6fucKbgq9UdUdJbXTtjviwAq187x/Sn9AhoSlxnHMBqYuN3l8igg2 +7m5BnC44PRaO5GerluZ1NrxydYgxp71JfF/eqiqDhVNBLwb3gorHNW2ON97htTYx +GdcSzxivAgMBAAECggEALDCRucYwQTmEexoWA94+aSXP/J4XLX23ImJs1bvVGccH +KblUVV3E9x36DN4oIEZ+eOpNC8mRC0FJiDjPB643kOQ8PvAMlNHKRjRZt/nw9KW/ +4ENtMm0GrIQCzdAaY9ritoeoRYwgMBvadcEKt8seEpsg+eWk9izOmeWY8DYvMw6N +hNu5zQLkMGTTqfDxkl7KnyKPhjW/++eUdgsTIA31/wHsJSiNR5Pkoy2fOVtNO7JN +EghcKE3lYKKzOW6vg0LBY8xVQ4KMbCVgnYNI3MU9qpG2bYxy1hlWIrsjrt9PyRp8 +jDSKnLD4Zvv4L6gj2fhelES/YQ/055YyzG801Q+gUQKBgQDohqr5fwQj8Awrc0K/ +DodaIkVwXHkQAhSWidrqxZXmtn4ZsgDA3V82ZTIjWe2v7ES5U4jeYKGoUweCUodr +PPT0IKEFmS2Fq1AZZx7Ry+ihA7gw6PV5DbG5DwyNYlhn6F6Bghl8pKAcXPGuwtgd +BKXj7utEp57Q9ue3P00cGNokKQKBgQDoNNFMPnfv5UQp+K0A89cKW8q6sf93/ul4 +kjh72q/KbK57ouhWPNib3tJLvkl7P8S45nrUGQZtd6zLhU/6SzAnGGnNZ7gNAs3l +SWidcmZDqIiIXh6BF4/4WxXMXJdhfrux9/O8Xk89v+EDAbLbN8jSrvy87+6mOmRM +r/MAXToxFwKBgHpGbtxalbPMRKoIp33OxxB32yoWBreLUIZFIfC5THWRW8hpWYoS +H0J8fpwmax5K0WzfZ6cBC6F3YAiBG6Mh3/IMwoAuJ8kV6D4jgwpx/vfE+/QEXSl2 +MRIOvtwObkzd3eyenIZ2D5g6rADphznjOtUcy21D8/kRDZLIX+U5kGTZAoGBAIYg +/ETuUJlh9V3JJyXFtBFntFLjPo4x0Oq0i6v/RkvHO4JvN4WY4AYpT5Aw+oEW9KtZ +dtnNGslgt49YEjqh886ha3wazVW2qPgozyUjT68FSth6hWRMF/19n7nMQiUu73x9 +nWzRjTQ+Aduav5WhQ39vVM5OSav7TrR9bgBn2ZVBAoGBAN4Hle/PIFzApQYQRIT0 +wPpOvEVx56+c70ZMvLv5UgmY2jLKZKFUV6oGGUZlJXfh1ZMnXShWY1pjvi/FnIIi +AKDB9N17DE5AmpzuXFjU3YwXde98MjuUY03P3yaFQ4cXYryqgZxuMPgyGFM9vtMd +WXFdvCtm0c3WMpPJSr9kgy6Q +-----END PRIVATE KEY----- diff --git a/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.py b/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.py new file mode 100644 index 000000000..18c6fbf9b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mail/files/smtpserver.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2018, Dag Wieers (@dagwieers) +# 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 asyncore +import os.path +import ssl +import sys + +# Handle TLS and non-TLS support +try: + import smtpd_tls + HAS_TLS = True +except ImportError: + import smtpd + HAS_TLS = False + print('Library smtpd-tls is missing or not supported, hence starttls is NOT supported.') + +# Handle custom ports +port = '25:465' +if len(sys.argv) > 1: + port = sys.argv[1] +ports = port.split(':') +if len(ports) > 1: + port1, port2 = int(ports[0]), int(ports[1]) +else: + port1, port2 = int(port), None + +# Handle custom certificate +basename = os.path.splitext(sys.argv[0])[0] +certfile = basename + '.crt' +if len(sys.argv) > 2: + certfile = sys.argv[2] + +# Handle custom key +keyfile = basename + '.key' +if len(sys.argv) > 3: + keyfile = sys.argv[3] + +try: + ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) +except AttributeError: + ssl_ctx = None + if HAS_TLS: + print('Python ssl library does not support SSLContext, hence starttls and TLS are not supported.') + import smtpd + +if HAS_TLS and ssl_ctx is not None: + print('Using %s and %s' % (certfile, keyfile)) + ssl_ctx.load_cert_chain(certfile=certfile, keyfile=keyfile) + + print('Start SMTP server on port', port1) + smtp_server1 = smtpd_tls.DebuggingServer(('127.0.0.1', port1), None, ssl_ctx=ssl_ctx, starttls=True) + if port2: + print('Start TLS SMTP server on port', port2) + smtp_server2 = smtpd_tls.DebuggingServer(('127.0.0.1', port2), None, ssl_ctx=ssl_ctx, starttls=False) +else: + print('Start SMTP server on port', port1) + smtp_server1 = smtpd.DebuggingServer(('127.0.0.1', port1), None) # pylint: disable=used-before-assignment + if port2: + print('WARNING: TLS is NOT supported on this system, not listening on port %s.' % port2) + +asyncore.loop() diff --git a/ansible_collections/community/general/tests/integration/targets/mail/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/mail/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mail/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/mail/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml new file mode 100644 index 000000000..4f3f90a51 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml @@ -0,0 +1,105 @@ +--- +#################################################################### +# 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 + +# 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 + +- name: Install test smtpserver + copy: + src: '{{ item }}' + dest: '{{ remote_tmp_dir }}/{{ item }}' + loop: + - smtpserver.py + - smtpserver.crt + - smtpserver.key + +# 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 basic test-mail + mail: + port: 10025 + subject: Test mail 1 (smtp) + 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 + +- 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 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 + +# 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: Sending mail using starttls failed. + when: smtpd_tls is succeeded and starttls_support is failed and tls_support is succeeded + +- 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/aliases b/ansible_collections/community/general/tests/integration/targets/mas/aliases new file mode 100644 index 000000000..ea236467f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mas/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 + +needs/root +unsupported 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 new file mode 100644 index 000000000..f659160dc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml @@ -0,0 +1,158 @@ +--- +#################################################################### +# 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 mas module. +# Copyright (c) 2020, Lukas Bestle +# 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 + +# Test preparation +- name: Uninstall Rested to ensure consistent starting point + mas: + id: 421879749 + state: absent + become: true + +- name: Determine whether the app is installed + stat: + path: /Applications/Rested.app + register: install_status + +- name: Ensure the app is uninstalled + assert: + that: + - install_status.stat.exists == false + +- name: Wait until the OS-internal cache was updated + pause: + seconds: 5 + +# Installation +- name: Check if Rested needs to be installed + mas: + id: 421879749 + state: present + register: install_check + check_mode: true + +- name: Ensure that the status would have changed + assert: + that: + - install_check is changed + - install_check.msg == "Installed 1 app(s)" + +- name: Determine whether the app is installed + stat: + path: /Applications/Rested.app + register: install_status + +- name: Ensure the app is not yet installed + assert: + that: + - install_status.stat.exists == false + +- name: Install Rested + mas: + id: 421879749 + state: present + register: install + +- name: Ensure that the status changed + assert: + that: + - install is changed + - install.msg == "Installed 1 app(s)" + +- name: Determine whether the app is installed + stat: + path: /Applications/Rested.app + register: install_status + +- name: Ensure the app is installed + assert: + that: + - install_status.stat.exists == true + +- name: Wait until the OS-internal cache was updated + pause: + seconds: 5 + +- name: Install Rested again + mas: + id: 421879749 + state: present + register: install_again + +- name: Ensure that the status is unchanged (already installed) + assert: + that: + - install_again is not changed + - "'msg' not in install_again" + +# Uninstallation +- name: Check if Rested needs to be uninstalled + mas: + id: 421879749 + state: absent + register: uninstall_check + become: true + check_mode: true + +- name: Ensure that the status would have changed + assert: + that: + - uninstall_check is changed + - uninstall_check.msg == "Uninstalled 1 app(s)" + +- name: Determine whether the app is installed + stat: + path: /Applications/Rested.app + register: install_status + +- name: Ensure the app is not yet uninstalled + assert: + that: + - install_status.stat.exists == true + +- name: Unistall Rested + mas: + id: 421879749 + state: absent + register: uninstall + become: true + +- name: Ensure that the status changed + assert: + that: + - uninstall is changed + - uninstall.msg == "Uninstalled 1 app(s)" + +- name: Determine whether the app is installed + stat: + path: /Applications/Rested.app + register: uninstall_status + +- name: Ensure the app is uninstalled + assert: + that: + - uninstall_status.stat.exists == false + +- name: Wait until the OS-internal cache was updated + pause: + seconds: 5 + +- name: Uninstall Rested again + mas: + id: 421879749 + state: absent + register: uninstall_again + become: true + +- name: Ensure that the status is unchanged (already uninstalled) + assert: + that: + - uninstall_again is not changed + - "'msg' not in uninstall_again" diff --git a/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/aliases b/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/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/memset_dns_reload/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/meta/main.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/meta/main.yml @@ -0,0 +1,4 @@ +--- +# 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/memset_dns_reload/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/tasks/main.yml new file mode 100644 index 000000000..153df95ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_dns_reload/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 # +#################################################################### + +# 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: request reload with invalid API key + memset_dns_reload: + api_key: "wa9aerahhie0eekee9iaphoorovooyia" + ignore_errors: true + register: result + +- name: check API response with invalid API key + assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: request reload and poll + memset_dns_reload: + api_key: "{{ api_key }}" + poll: true + register: result + +- name: check reload succeeded + assert: + that: + - result is changed + - result is successful diff --git a/ansible_collections/community/general/tests/integration/targets/memset_memstore_info/aliases b/ansible_collections/community/general/tests/integration/targets/memset_memstore_info/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_memstore_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/memset_memstore_info/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_memstore_info/meta/main.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_memstore_info/meta/main.yml @@ -0,0 +1,4 @@ +--- +# 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/memset_memstore_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_memstore_info/tasks/main.yml new file mode 100644 index 000000000..7dc7f7c69 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_memstore_info/tasks/main.yml @@ -0,0 +1,34 @@ +--- +#################################################################### +# 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: query API with invalid API key + memset_memstore_info: + api_key: 'wa9aerahhie0eekee9iaphoorovooyia' + name: 'mstestyaa1' + ignore_errors: true + register: result + +- name: check API response with invalid API key + assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: request memstore infos + memset_memstore_info: + api_key: "{{ api_key }}" + name: 'mstestyaa1' + register: result + +- name: check the request succeeded + assert: + that: + - result is not changed + - result is successful diff --git a/ansible_collections/community/general/tests/integration/targets/memset_server_info/aliases b/ansible_collections/community/general/tests/integration/targets/memset_server_info/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_server_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/memset_server_info/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_server_info/meta/main.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_server_info/meta/main.yml @@ -0,0 +1,4 @@ +--- +# 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/memset_server_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_server_info/tasks/main.yml new file mode 100644 index 000000000..79066fac7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_server_info/tasks/main.yml @@ -0,0 +1,34 @@ +--- +#################################################################### +# 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: query API with invalid API key + memset_server_info: + api_key: 'wa9aerahhie0eekee9iaphoorovooyia' + name: 'testyaa1' + ignore_errors: true + register: result + +- name: check API response with invalid API key + assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: request server infos + memset_server_info: + api_key: "{{ api_key }}" + name: 'testyaa1' + register: result + +- name: check the request succeeded + assert: + that: + - result is not changed + - result is successful diff --git a/ansible_collections/community/general/tests/integration/targets/memset_zone/aliases b/ansible_collections/community/general/tests/integration/targets/memset_zone/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone/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/memset_zone/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone/meta/main.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone/meta/main.yml @@ -0,0 +1,4 @@ +--- +# 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/memset_zone/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone/tasks/main.yml new file mode 100644 index 000000000..091bab823 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone/tasks/main.yml @@ -0,0 +1,125 @@ +--- +#################################################################### +# 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: create random string + set_fact: + zone_name: "{{ 65535 | random | string }}.ansible.example.com" + +- name: create zone with incorrect API key + memset_zone: + api_key: "wa9aerahhie0eekee9iaphoorovooyia" + state: present + name: "{{ zone_name }}" + ttl: 300 + ignore_errors: true + register: result + +- name: check API response with invalid API key + assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: test creating zone + memset_zone: + api_key: "{{ api_key }}" + state: present + name: "{{ zone_name }}" + ttl: 300 + check_mode: true + register: result + +- name: check if the zone would be created + assert: + that: + - result is changed + - result is successful + +- name: create zone + memset_zone: + api_key: "{{ api_key }}" + state: present + name: "{{ zone_name }}" + ttl: 300 + register: result + +- name: create the zone + assert: + that: + - result is changed + - result is successful + +- name: create duplicate zone + memset_zone: + api_key: "{{ api_key }}" + state: present + name: "{{ zone_name }}" + ttl: 300 + register: result + +- name: ensure we can't create duplicate zones + assert: + that: + - result is not changed + +- name: test deleting zone + memset_zone: + api_key: "{{ api_key }}" + state: absent + name: "{{ zone_name }}" + check_mode: true + register: result + +- name: check if the zone would be deleted + assert: + that: + - result is changed + - result is successful + +- name: delete empty zone + memset_zone: + api_key: "{{ api_key }}" + state: absent + name: "{{ zone_name }}" + force: false + register: result + +- name: delete the zone + assert: + that: + - result is changed + - result is successful + +- name: create zone for deletion test + memset_zone: + api_key: "{{ api_key }}" + state: present + name: "{{ zone_name }}" + register: result + +- name: ensure we can't create duplicate zones + assert: + that: + - result is changed + - result is successful + +- name: delete zone with force + memset_zone: + api_key: "{{ api_key }}" + state: absent + name: "{{ zone_name }}" + force: true + register: result + +- name: ensure force is respected + assert: + that: + - result is changed + - result is successful diff --git a/ansible_collections/community/general/tests/integration/targets/memset_zone/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone/vars/main.yml new file mode 100644 index 000000000..8828011b0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone/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 + +random_string: "baiqui8ci6miedoo9eivohJ0aixei7oo" diff --git a/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/aliases b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/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/memset_zone_domain/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/meta/main.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/meta/main.yml @@ -0,0 +1,4 @@ +--- +# 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/memset_zone_domain/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/tasks/main.yml new file mode 100644 index 000000000..e92ffcdcf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/tasks/main.yml @@ -0,0 +1,152 @@ +--- +#################################################################### +# 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: create domain with invalid API key + memset_zone_domain: + api_key: "wa9aerahhie0eekee9iaphoorovooyia" + state: present + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + ignore_errors: true + register: result + +- name: check API response with invalid API key + assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: create domain over 250 chars + memset_zone_domain: + api_key: "{{ api_key }}" + state: present + domain: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com' + zone: "{{ target_zone }}" + ignore_errors: true + register: result + +- name: test domain length is validated + assert: + that: + - result is failed + - "'Zone domain must be less than 250 characters in length' in result.stderr" + +- name: create domain in non-existent zone + memset_zone_domain: + api_key: "{{ api_key }}" + state: present + domain: "{{ test_domain }}" + zone: "non-existent-zone" + ignore_errors: true + register: result + +- name: fail if zone does not exist + assert: + that: + - result is failed + - "'does not exist, cannot create domain.' in result.stderr" + +- name: create domain in non-unique zone + memset_zone_domain: + api_key: "{{ api_key }}" + state: present + domain: "{{ test_domain }}" + zone: "{{ duplicate_zone }}" + ignore_errors: true + register: result + +- name: fail if the zone is not unique + assert: + that: + - result is failed + - "'matches multiple zones, cannot create domain' in result.stderr" + +- name: test creating domain + memset_zone_domain: + api_key: "{{ api_key }}" + state: present + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + check_mode: true + register: result + +- name: create domain with check mode + assert: + that: + - result is changed + - result is successful + +- name: create domain + memset_zone_domain: + api_key: "{{ api_key }}" + state: present + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + register: result + +- name: create domain + assert: + that: + - result is changed + - result is successful + +- name: create existing domain + memset_zone_domain: + api_key: "{{ api_key }}" + state: present + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + register: result + +- name: create existing domain + assert: + that: + - result is not changed + +- name: test deleting domain + memset_zone_domain: + api_key: "{{ api_key }}" + state: absent + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + check_mode: true + register: result + +- name: delete domain with check mode + assert: + that: + - result is changed + - result is successful + +- name: delete domain + memset_zone_domain: + api_key: "{{ api_key }}" + state: absent + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + register: result + +- name: delete domain + assert: + that: + - result is changed + +- name: delete non-existent domain + memset_zone_domain: + api_key: "{{ api_key }}" + state: absent + domain: "{{ test_domain }}" + zone: "{{ target_zone }}" + register: result + +- name: delete absent domain + assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/vars/main.yml new file mode 100644 index 000000000..e891ff49f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_domain/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 + +test_domain: ansible.example.com +target_zone: ansible-dns-zone +duplicate_zone: ansible-dns-zone-dupe diff --git a/ansible_collections/community/general/tests/integration/targets/memset_zone_record/aliases b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/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/memset_zone_record/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/meta/main.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/meta/main.yml @@ -0,0 +1,4 @@ +--- +# 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/memset_zone_record/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/tasks/main.yml new file mode 100644 index 000000000..c1bdd6873 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/tasks/main.yml @@ -0,0 +1,235 @@ +--- +#################################################################### +# 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: create record with incorrect API key + memset_zone_record: + api_key: "wa9aerahhie0eekee9iaphoorovooyia" + state: present + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + ignore_errors: true + register: result + +- assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: create record in non-existent zone + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "a-non-existent-zone" + type: A + address: 127.0.0.1 + ignore_errors: true + register: result + +- name: assert that record is not created + assert: + that: + - "'DNS zone a-non-existent-zone does not exist.' in result.msg" + - result is not successful + +- name: create record in non-unique zone + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ duplicate_zone }}" + type: A + address: 127.0.0.1 + ignore_errors: true + register: result + +- name: assert that record is not created + assert: + that: + - "'ansible-dns-zone-dupe matches multiple zones.' in result.msg" + - result is not successful + +- name: create record with invalid priority + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: SRV + address: "0 5269 hostname.example.com" + record: "_jabber._tcp" + priority: 1001 + ignore_errors: true + register: result + +- name: assert that priority was out of range + assert: + that: + - "'Priority must be in the range 0 > 999 (inclusive).' in result.msg" + - result is not successful + +- name: create record with address longer than 250 chars + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: CNAME + address: "aaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com" + record: "aaa.ansible.com" + ignore_errors: true + register: result + +- name: assert that address was longer than allowed + assert: + that: + - "'Address must be less than 250 characters in length.' in result.msg" + - result is not successful + +- name: create record longer than 63 chars + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + record: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ignore_errors: true + register: result + +- name: assert that record was longer than allowed + assert: + that: + - "'Record must be less than 63 characters in length.' in result.msg" + - result is not successful + +- name: create record which cannot have relative enabled + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + relative: true + ignore_errors: true + register: result + +- name: assert that setting relative failed + assert: + that: + - "'Relative is only valid for CNAME, MX, NS and SRV record types' in result.msg" + - result is not successful + +- name: test creating valid A record + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + record: "www" + check_mode: true + register: result + +- name: assert that result would have changed + assert: + that: + - result is changed + - result is successful + +- name: actually create valid A record + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + record: "www" + register: result + +- name: assert that result changed + assert: + that: + - result is changed + - result is successful + +- name: create valid SPF record + memset_zone_record: + api_key: "{{ api_key }}" + state: present + zone: "{{ test_zone }}" + type: TXT + address: "v=spf1 +a +mx +ip4:127.0.0.1 ?all" + register: result + +- name: assert that result changed + assert: + that: + - result is changed + - result is successful + +- name: test deleting A record + memset_zone_record: + api_key: "{{ api_key }}" + state: absent + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + record: "www" + check_mode: true + register: result + +- name: assert that result changed + assert: + that: + - result is changed + - result is successful + +- name: actually delete A record + memset_zone_record: + api_key: "{{ api_key }}" + state: absent + zone: "{{ test_zone }}" + type: A + address: 127.0.0.1 + record: "www" + register: result + +- name: assert that result changed + assert: + that: + - result is changed + - result is successful + +- name: delete SPF record + memset_zone_record: + api_key: "{{ api_key }}" + state: absent + zone: "{{ test_zone }}" + type: TXT + address: "v=spf1 +a +mx +ip4:127.0.0.1 ?all" + register: result + +- name: assert that result changed + assert: + that: + - result is changed + - result is successful + +- name: delete non-existent SPF record + memset_zone_record: + api_key: "{{ api_key }}" + state: absent + zone: "{{ test_zone }}" + type: TXT + address: "v=spf1 +a +mx +ip4:127.0.0.1 ?all" + register: result + +- name: assert that result changed + assert: + that: + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/memset_zone_record/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/vars/main.yml new file mode 100644 index 000000000..857f1c722 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/memset_zone_record/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 + +test_zone: ansible-dns-record-tests +duplicate_zone: ansible-dns-zone-dupe diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/aliases b/ansible_collections/community/general/tests/integration/targets/module_helper/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/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/module_helper/library/mdepfail.py b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py new file mode 100644 index 000000000..92ebbde6e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py @@ -0,0 +1,70 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Alexei Znamensky +# +# 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 + +DOCUMENTATION = ''' +module: mdepfail +author: "Alexei Znamensky (@russoz)" +short_description: Simple module for testing +description: + - Simple module test description. +options: + a: + description: aaaa + type: int + b: + description: bbbb + type: str + c: + description: cccc + type: str +''' + +EXAMPLES = "" + +RETURN = "" + +from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper +from ansible.module_utils.basic import missing_required_lib + +with ModuleHelper.dependency("nopackagewiththisname", missing_required_lib("nopackagewiththisname")): + import nopackagewiththisname # noqa: F401, pylint: disable=unused-import + + +class MSimple(ModuleHelper): + output_params = ('a', 'b', 'c') + module = dict( + argument_spec=dict( + a=dict(type='int'), + b=dict(type='str'), + c=dict(type='str'), + ), + ) + + def __init_module__(self): + self.vars.set('value', None) + self.vars.set('abc', "abc", diff=True) + + def __run__(self): + if (0 if self.vars.a is None else self.vars.a) >= 100: + raise Exception("a >= 100") + if self.vars.c == "abc change": + self.vars['abc'] = "changed abc" + if self.vars.get('a', 0) == 2: + self.vars['b'] = str(self.vars.b) * 2 + self.vars['c'] = str(self.vars.c) * 2 + + +def main(): + msimple = MSimple() + msimple.run() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/library/msimple.py b/ansible_collections/community/general/tests/integration/targets/module_helper/library/msimple.py new file mode 100644 index 000000000..096e51524 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/library/msimple.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Alexei Znamensky +# +# 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 + +DOCUMENTATION = ''' +module: msimple +author: "Alexei Znamensky (@russoz)" +short_description: Simple module for testing +description: + - Simple module test description. +options: + a: + description: aaaa + type: int + b: + description: bbbb + type: str + c: + description: cccc + type: str +''' + +EXAMPLES = "" + +RETURN = "" + +from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper +from ansible_collections.community.general.plugins.module_utils.mh.deco import check_mode_skip + + +class MSimple(ModuleHelper): + output_params = ('a', 'b', 'c', 'm') + module = dict( + argument_spec=dict( + a=dict(type='int', default=0), + b=dict(type='str'), + c=dict(type='str'), + m=dict(type='str'), + ), + supports_check_mode=True, + ) + + def __init_module__(self): + self.vars.set('value', None) + self.vars.set('abc', "abc", diff=True) + + @check_mode_skip + def process_a3_bc(self): + if self.vars.a == 3: + self.vars['b'] = str(self.vars.b) * 3 + self.vars['c'] = str(self.vars.c) * 3 + + def __run__(self): + if self.vars.m: + self.vars.msg = self.vars.m + if self.vars.a >= 100: + raise Exception("a >= 100") + if self.vars.c == "abc change": + self.vars['abc'] = "changed abc" + if self.vars.get('a', 0) == 2: + self.vars['b'] = str(self.vars.b) * 2 + self.vars['c'] = str(self.vars.c) * 2 + self.process_a3_bc() + + +def main(): + msimple = MSimple() + msimple.run() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/library/msimpleda.py b/ansible_collections/community/general/tests/integration/targets/module_helper/library/msimpleda.py new file mode 100644 index 000000000..c21c3d2ea --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/library/msimpleda.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Alexei Znamensky +# +# 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 + +DOCUMENTATION = ''' +module: msimpleda +author: "Alexei Znamensky (@russoz)" +short_description: Simple module for testing DeprecationAttrsMixin +description: + - Simple module test description. +options: + a: + description: aaaa + type: int +''' + +EXAMPLES = "" + +RETURN = "" + +from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper +from ansible_collections.community.general.plugins.module_utils.mh.mixins.deprecate_attrs import ( # noqa: F401, pylint: disable=unused-import + DeprecateAttrsMixin +) + + +class MSimpleDA(ModuleHelper): + output_params = ('a',) + module = dict( + argument_spec=dict( + a=dict(type='int'), + ), + ) + + attr1 = "abc" + attr2 = "def" + + def __init_module__(self): + self._deprecate_attr( + "attr2", + msg="Attribute attr2 is deprecated", + version="9.9.9", + collection_name="community.general", + target=self.__class__, + module=self.module, + ) + + def __run__(self): + if self.vars.a == 1: + self.vars.attr1 = self.attr1 + if self.vars.a == 2: + self.vars.attr2 = self.attr2 + + +def main(): + MSimpleDA.execute() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py new file mode 100644 index 000000000..bfaab0375 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Alexei Znamensky +# +# 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 + +DOCUMENTATION = ''' +module: mstate +author: "Alexei Znamensky (@russoz)" +short_description: State-based module for testing +description: + - State-based module test description. +options: + a: + description: aaaa + type: int + required: true + b: + description: bbbb + type: str + c: + description: cccc + type: str + state: + description: test states + type: str + choices: [join, b_x_a, c_x_a, both_x_a] + default: join +''' + +EXAMPLES = "" + +RETURN = "" + +from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper + + +class MState(StateModuleHelper): + output_params = ('a', 'b', 'c', 'state') + module = dict( + argument_spec=dict( + a=dict(type='int', required=True), + b=dict(type='str'), + c=dict(type='str'), + state=dict(type='str', choices=['join', 'b_x_a', 'c_x_a', 'both_x_a', 'nop'], default='join'), + ), + ) + + def __init_module__(self): + self.vars.set('result', "abc", diff=True) + + def state_join(self): + self.vars['result'] = "".join([str(self.vars.a), str(self.vars.b), str(self.vars.c)]) + + def state_b_x_a(self): + self.vars['result'] = str(self.vars.b) * self.vars.a + + def state_c_x_a(self): + self.vars['result'] = str(self.vars.c) * self.vars.a + + def state_both_x_a(self): + self.vars['result'] = (str(self.vars.b) + str(self.vars.c)) * self.vars.a + + def state_nop(self): + pass + + +def main(): + mstate = MState() + mstate.run() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/main.yml new file mode 100644 index 000000000..2368cfcbb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/main.yml @@ -0,0 +1,9 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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: msimple.yml +- include_tasks: msimple_output_conflict.yml +- include_tasks: mdepfail.yml +- include_tasks: mstate.yml +- include_tasks: msimpleda.yml diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mdepfail.yml b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mdepfail.yml new file mode 100644 index 000000000..1655be54e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mdepfail.yml @@ -0,0 +1,18 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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 failing dependency + mdepfail: + a: 123 + ignore_errors: true + register: result + +- name: assert failing dependency + assert: + that: + - result is failed + - '"Failed to import" in result.msg' + - '"nopackagewiththisname" in result.msg' + - '"ModuleNotFoundError:" in result.exception or "ImportError:" in result.exception' + - '"nopackagewiththisname" in result.exception' diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple.yml b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple.yml new file mode 100644 index 000000000..03818639c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple.yml @@ -0,0 +1,85 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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 msimple (set a=80) + msimple: + a: 80 + register: simple1 + +- name: assert simple1 + assert: + that: + - simple1.a == 80 + - simple1.abc == "abc" + - simple1 is not changed + - simple1.value is none + +- name: test msimple 2 + msimple: + a: 101 + ignore_errors: true + register: simple2 + +- name: assert simple2 + assert: + that: + - simple2.a == 101 + - 'simple2.msg == "Module failed with exception: a >= 100"' + - simple2.abc == "abc" + - simple2 is failed + - simple2 is not changed + - simple2.value is none + +- name: test msimple 3 + msimple: + a: 2 + b: potatoes + register: simple3 + +- name: assert simple3 + assert: + that: + - simple3.a == 2 + - simple3.b == "potatoespotatoes" + - simple3.c == "NoneNone" + - simple3 is not changed + +- name: test msimple 4 + msimple: + c: abc change + register: simple4 + +- name: assert simple4 + assert: + that: + - simple4.c == "abc change" + - simple4.abc == "changed abc" + - simple4 is changed + +- name: test msimple 5a + msimple: + a: 3 # should triple b and c + b: oh + c: my + register: simple5a + +- name: test msimple 5b + check_mode: true + msimple: + a: 3 # should triple b and c + b: oh + c: my + register: simple5b + +- name: assert simple5 + assert: + that: + - simple5a.a == 3 + - simple5a.b == "ohohoh" + - simple5a.c == "mymymy" + - simple5a is not changed + - simple5b.a == 3 + - simple5b.b == "oh" + - simple5b.c == "my" + - simple5b is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple_output_conflict.yml b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple_output_conflict.yml new file mode 100644 index 000000000..62fe1e327 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimple_output_conflict.yml @@ -0,0 +1,54 @@ +# Copyright (c) 2023, Alexei Znamensky +# 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 msimple conflict output (set a=80) + msimple: + a: 80 + register: simple1 + +- name: assert simple1 + assert: + that: + - simple1.a == 80 + - simple1.abc == "abc" + - simple1 is not changed + - simple1.value is none + +- name: test msimple conflict output 2 + msimple: + a: 80 + m: a message in a bottle + register: simple2 + +- name: assert simple2 + assert: + that: + - simple1.a == 80 + - simple1.abc == "abc" + - simple1 is not changed + - simple1.value is none + - > + "_msg" not in simple2 + - > + simple2.msg == "a message in a bottle" + +- name: test msimple 3 + msimple: + a: 101 + m: a message in a bottle + ignore_errors: true + register: simple3 + +- name: assert simple3 + assert: + that: + - simple3.a == 101 + - > + simple3.msg == "Module failed with exception: a >= 100" + - > + simple3._msg == "a message in a bottle" + - simple3.abc == "abc" + - simple3 is failed + - simple3 is not changed + - simple3.value is none diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimpleda.yml b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimpleda.yml new file mode 100644 index 000000000..e01b65e12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/msimpleda.yml @@ -0,0 +1,39 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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_fact: + attr2_d: + msg: Attribute attr2 is deprecated + version: 9.9.9 + collection_name: community.general + attr2_d_29: + msg: Attribute attr2 is deprecated + version: 9.9.9 +- set_fact: + attr2_depr_dict: "{{ ((ansible_version.major, ansible_version.minor) < (2, 10))|ternary(attr2_d_29, attr2_d) }}" + +- name: test msimpleda 1 + msimpleda: + a: 1 + register: simple1 + +- name: assert simple1 + assert: + that: + - simple1.a == 1 + - simple1.attr1 == "abc" + - ("deprecations" not in simple1) or attr2_depr_dict not in simple1.deprecations + +- name: test msimpleda 2 + msimpleda: + a: 2 + register: simple2 + +- name: assert simple2 + assert: + that: + - simple2.a == 2 + - simple2.attr2 == "def" + - '"deprecations" in simple2' + - attr2_depr_dict in simple2.deprecations diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mstate.yml b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mstate.yml new file mode 100644 index 000000000..40c695f6d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/tasks/mstate.yml @@ -0,0 +1,83 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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 mstate 1 + mstate: + a: 80 + b: banana + c: cashew + state: nop + register: state1 + +- name: assert state1 + assert: + that: + - state1.a == 80 + - state1.b == "banana" + - state1.c == "cashew" + - state1.result == "abc" + - state1 is not changed + +- name: test mstate 2 + mstate: + a: 80 + b: banana + c: cashew + register: state2 + +- name: assert state2 + assert: + that: + - state2.a == 80 + - state2.b == "banana" + - state2.c == "cashew" + - state2.result == "80bananacashew" + - state2 is changed + +- name: test mstate 3 + mstate: + a: 3 + b: banana + state: b_x_a + register: state3 + +- name: assert state3 + assert: + that: + - state3.a == 3 + - state3.b == "banana" + - state3.result == "bananabananabanana" + - state3 is changed + +- name: test mstate 4 + mstate: + a: 4 + c: cashew + state: c_x_a + register: state4 + +- name: assert state4 + assert: + that: + - state4.a == 4 + - state4.c == "cashew" + - state4.result == "cashewcashewcashewcashew" + - state4 is changed + +- name: test mstate 5 + mstate: + a: 5 + b: foo + c: bar + state: both_x_a + register: state5 + +- name: assert state5 + assert: + that: + - state5.a == 5 + - state5.b == "foo" + - state5.c == "bar" + - state5.result == "foobarfoobarfoobarfoobarfoobar" + - state5 is changed diff --git a/ansible_collections/community/general/tests/integration/targets/monit/aliases b/ansible_collections/community/general/tests/integration/targets/monit/aliases new file mode 100644 index 000000000..ca39d1353 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/aliases @@ -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 + +azp/posix/2 +destructive +needs/target/setup_epel +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/monit/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/monit/defaults/main.yml new file mode 100644 index 000000000..ec064643c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/defaults/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 + +process_root: /opt/httpd_echo +process_file: "{{ process_root }}/httpd_echo.py" +process_venv: "{{ process_root }}/venv" +process_run_cmd: "{{ process_venv }}/bin/python {{ process_file }}" diff --git a/ansible_collections/community/general/tests/integration/targets/monit/files/httpd_echo.py b/ansible_collections/community/general/tests/integration/targets/monit/files/httpd_echo.py new file mode 100644 index 000000000..cd77da26b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/files/httpd_echo.py @@ -0,0 +1,51 @@ +# Copyright (c) 2020, Simon Kelly +# 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 daemon + +try: + from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer + + def write_to_output(stream, content): + stream.write(content) +except ImportError: + from http.server import BaseHTTPRequestHandler, HTTPServer + + def write_to_output(stream, content): + stream.write(bytes(content, "utf-8")) + + +hostname = "localhost" +server_port = 8082 + + +class EchoServer(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header("Content-type", "text/plain") + self.end_headers() + write_to_output(self.wfile, self.path) + + +def run_webserver(): + webServer = HTTPServer((hostname, server_port), EchoServer) + print("Server started http://%s:%s" % (hostname, server_port)) + + try: + webServer.serve_forever() + except KeyboardInterrupt: + pass + + webServer.server_close() + print("Server stopped.") + + +if __name__ == "__main__": + context = daemon.DaemonContext() + + with context: + run_webserver() diff --git a/ansible_collections/community/general/tests/integration/targets/monit/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/monit/meta/main.yml new file mode 100644 index 000000000..2d6cafb56 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/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_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/monit/tasks/check_state.yml b/ansible_collections/community/general/tests/integration/targets/monit/tasks/check_state.yml new file mode 100644 index 000000000..bc8eb7c81 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/tasks/check_state.yml @@ -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 + +- name: "{{ reason }} ('up')" + command: "curl -sf http://localhost:8082/hello" + when: service_state == 'up' + register: curl_result + until: not curl_result.failed + retries: 5 + delay: 1 + +- name: "{{ reason }} ('down')" + command: "curl -sf http://localhost:8082/hello" + register: curl_result + failed_when: curl_result == 0 + when: service_state == 'down' + until: not curl_result.failed + retries: 5 + delay: 1 diff --git a/ansible_collections/community/general/tests/integration/targets/monit/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/monit/tasks/main.yml new file mode 100644 index 000000000..ea8595412 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/tasks/main.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 # +#################################################################### + +# 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: Install EPEL repository (RHEL only) + include_role: + name: setup_epel + when: + - ansible_distribution in ['RedHat', 'CentOS'] + - ansible_distribution_major_version is version('9', '<') + + - name: create required directories + become: true + file: + path: "{{ item }}" + state: directory + loop: + - /var/lib/monit + - /var/run/monit + - "{{ process_root }}" + + - name: install monit + become: true + package: + name: monit + state: present + + - include_vars: '{{ item }}' + with_first_found: + - files: + - "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml" + - '{{ ansible_os_family }}.yml' + - 'defaults.yml' + + - name: monit config + become: true + template: + src: "monitrc.j2" + dest: "{{ monitrc }}" + + - name: copy process file + become: true + copy: + src: httpd_echo.py + dest: "{{ process_file }}" + + - name: Install virtualenv on CentOS 8 + package: + name: virtualenv + state: present + when: ansible_distribution == 'CentOS' and ansible_distribution_major_version == '8' + + - name: Install virtualenv on Arch Linux + pip: + name: virtualenv + state: present + when: ansible_os_family == 'Archlinux' + + - name: install dependencies + pip: + name: "{{ item }}" + virtualenv: "{{ process_venv }}" + extra_args: "-c {{ remote_constraints }}" + loop: + - setuptools==44 + - python-daemon + + - name: restart monit + become: true + service: + name: monit + state: restarted + + - include_tasks: test.yml + + always: + - name: stop monit + become: true + service: + name: monit + state: stopped + + - name: uninstall monit + become: true + package: + name: monit + state: absent + + - name: remove process files + file: + path: "{{ process_root }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/monit/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test.yml new file mode 100644 index 000000000..42fd033c7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test.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 + +# order is important +- import_tasks: test_reload_present.yml + +- import_tasks: test_state.yml + vars: + state: stopped + initial_state: up + expected_state: down + +- import_tasks: test_state.yml + vars: + state: started + initial_state: down + expected_state: up + +- import_tasks: test_state.yml + vars: + state: unmonitored + initial_state: up + expected_state: down + +- import_tasks: test_state.yml + vars: + state: monitored + initial_state: down + expected_state: up + +- import_tasks: test_errors.yml diff --git a/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_errors.yml b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_errors.yml new file mode 100644 index 000000000..362672387 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_errors.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 + +- name: Check an error occurs when wrong process name is used + monit: + name: missing + state: started + register: result + failed_when: result is not skip and (result is success or result is not failed) diff --git a/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_reload_present.yml b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_reload_present.yml new file mode 100644 index 000000000..0bd6cd073 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_reload_present.yml @@ -0,0 +1,65 @@ +--- +# 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: reload monit when process is missing + monit: + name: httpd_echo + state: reloaded + register: result + +- name: check that state is changed + assert: + that: + - result is success + - result is changed + +- name: test process not present + monit: + name: httpd_echo + state: present + timeout: 5 + register: result + failed_when: result is not skip and result is success + +- name: test monitor missing process + monit: + name: httpd_echo + state: monitored + register: result + failed_when: result is not skip and result is success + +- name: start process + shell: "{{ process_run_cmd }}" + +- import_tasks: check_state.yml + vars: + reason: verify service running + service_state: "up" + +- name: add process config + blockinfile: + path: "{{ monitrc }}" + block: | + check process httpd_echo with matching "httpd_echo" + start program = "{{ process_run_cmd }}" + stop program = "/bin/sh -c 'kill `pgrep -f httpd_echo`'" + if failed host localhost port 8082 then restart + +- name: restart monit + service: + name: monit + state: restarted + +- name: test process present again + monit: + name: httpd_echo + state: present + register: result + +- name: check that state is unchanged + assert: + that: + - result is success + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_state.yml b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_state.yml new file mode 100644 index 000000000..33a70c196 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/tasks/test_state.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 + +- import_tasks: check_state.yml + vars: + reason: verify initial service state + service_state: "{{ initial_state }}" + +- name: change httpd_echo process state to {{ state }} + monit: + name: httpd_echo + state: "{{ state }}" + register: result + +- name: check that state changed + assert: + that: + - result is success + - result is changed + +- import_tasks: check_state.yml + vars: + reason: check service state after action + service_state: "{{ expected_state }}" + +- name: try change state again to {{ state }} + monit: + name: httpd_echo + state: "{{ state }}" + register: result + +- name: check that state is not changed + assert: + that: + - result is success + - result is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/monit/templates/monitrc.j2 b/ansible_collections/community/general/tests/integration/targets/monit/templates/monitrc.j2 new file mode 100644 index 000000000..4f1a6e247 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/templates/monitrc.j2 @@ -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 +#} + +set daemon 2 +set logfile /var/log/monit.log +set idfile /var/lib/monit/id +set statefile /var/lib/monit/state +set pidfile /var/run/monit.pid + +set eventqueue + basedir /var/lib/monit/events + slots 100 + +set httpd port 2812 and + use address localhost + allow localhost diff --git a/ansible_collections/community/general/tests/integration/targets/monit/vars/Alpine.yml b/ansible_collections/community/general/tests/integration/targets/monit/vars/Alpine.yml new file mode 100644 index 000000000..773c9d985 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/vars/Alpine.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 + +monitrc: "/etc/monitrc" diff --git a/ansible_collections/community/general/tests/integration/targets/monit/vars/Archlinux.yml b/ansible_collections/community/general/tests/integration/targets/monit/vars/Archlinux.yml new file mode 100644 index 000000000..773c9d985 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/vars/Archlinux.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 + +monitrc: "/etc/monitrc" diff --git a/ansible_collections/community/general/tests/integration/targets/monit/vars/CentOS-6.yml b/ansible_collections/community/general/tests/integration/targets/monit/vars/CentOS-6.yml new file mode 100644 index 000000000..9ff9c2641 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/vars/CentOS-6.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 + +monitrc: "/etc/monit.conf" diff --git a/ansible_collections/community/general/tests/integration/targets/monit/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/monit/vars/RedHat.yml new file mode 100644 index 000000000..773c9d985 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/vars/RedHat.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 + +monitrc: "/etc/monitrc" diff --git a/ansible_collections/community/general/tests/integration/targets/monit/vars/Suse.yml b/ansible_collections/community/general/tests/integration/targets/monit/vars/Suse.yml new file mode 100644 index 000000000..773c9d985 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/vars/Suse.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 + +monitrc: "/etc/monitrc" diff --git a/ansible_collections/community/general/tests/integration/targets/monit/vars/defaults.yml b/ansible_collections/community/general/tests/integration/targets/monit/vars/defaults.yml new file mode 100644 index 000000000..74c76c7c9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/monit/vars/defaults.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 + +monitrc: "/etc/monit/monitrc" diff --git a/ansible_collections/community/general/tests/integration/targets/mqtt/aliases b/ansible_collections/community/general/tests/integration/targets/mqtt/aliases new file mode 100644 index 000000000..c25e0b6d5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mqtt/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/osx +skip/macos +skip/freebsd +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/mqtt/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/mqtt/meta/main.yml new file mode 100644 index 000000000..a9c2068ed --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mqtt/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_mosquitto diff --git a/ansible_collections/community/general/tests/integration/targets/mqtt/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/mqtt/tasks/main.yml new file mode 100644 index 000000000..0beb1b3b2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mqtt/tasks/main.yml @@ -0,0 +1,14 @@ +--- +#################################################################### +# 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 + +- include_tasks: ubuntu.yml + when: + - ansible_distribution == 'Ubuntu' + - ansible_distribution_release not in ['focal', 'jammy'] diff --git a/ansible_collections/community/general/tests/integration/targets/mqtt/tasks/ubuntu.yml b/ansible_collections/community/general/tests/integration/targets/mqtt/tasks/ubuntu.yml new file mode 100644 index 000000000..0c0a12d04 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mqtt/tasks/ubuntu.yml @@ -0,0 +1,147 @@ +--- +# 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 pip packages + pip: + name: paho-mqtt>=1.4.0 + state: present + +- name: MQTT non-TLS endpoint + mqtt: + topic: /node/s/bar/blurb + payload: foo + qos: 1 + client_id: me001 + register: result + +- assert: + that: + - result is success + +- name: Send a test message to TLS1.1 endpoint, no client version specified + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + port: 8883 + register: result + +- assert: + that: + - result is success + +- name: Send a test message to TLS1.2 endpoint, no client version specified + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + port: 8884 + register: result + +- assert: + that: + - result is success + +# TODO(Uncomment when TLS1.3 is supported in moquitto and ubuntu version) +# +# - name: Send a test message to TLS1.3 endpoint +# mqtt: +# topic: /node/s/bar/blurb +# payload: foo-tls +# qos: 1 +# client_id: me001 +# ca_certs: /tls/ca_certificate.pem +# certfile: /tls/client_certificate.pem +# keyfile: /tls/client_key.pem +# port: 8885 +# register: result + +#- assert: +# that: +# - result is success + +- name: Send a message, client TLS1.1, server (required) TLS1.2 - Expected failure + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + tls_version: tlsv1.1 + port: 8884 + register: result + failed_when: result is success + +- assert: + that: + - result is success + +# TODO(Uncomment when TLS1.3 is supported in moquitto and ubuntu version) +# +# - name: Send a message, client TLS1.1, server (required) TLS1.3 - Expected failure +# mqtt: +# topic: /node/s/bar/blurb +# payload: foo-tls +# qos: 1 +# client_id: me001 +# ca_certs: /tls/ca_certificate.pem +# certfile: /tls/client_certificate.pem +# keyfile: /tls/client_key.pem +# tls_version: tlsv1.1 +# port: 8885 +# register: result +# failed_when: result is success + +# - assert: +# that: +# - result is success + +- name: Send a message, client TLS1.2, server (required) TLS1.1 - Expected failure + mqtt: + topic: /node/s/bar/blurb + payload: foo-tls + qos: 1 + client_id: me001 + ca_certs: /tls/ca_certificate.pem + certfile: /tls/client_certificate.pem + keyfile: /tls/client_key.pem + tls_version: tlsv1.2 + port: 8883 + register: result + failed_when: result is success + +- assert: + that: + - result is success + +# TODO(Uncomment when TLS1.3 is supported in moquitto and ubuntu version) +# +# - name: Send a message, client TLS1.2, server (required) TLS1.3 - Expected failure +# mqtt: +# topic: /node/s/bar/blurb +# payload: foo-tls +# qos: 1 +# client_id: me001 +# ca_certs: /tls/ca_certificate.pem +# certfile: /tls/client_certificate.pem +# keyfile: /tls/client_key.pem +# tls_version: tlsv1.2 +# port: 8885 +# register: result +# failed_when: result is success + +# - assert: +# that: +# - result is success diff --git a/ansible_collections/community/general/tests/integration/targets/mssql_script/aliases b/ansible_collections/community/general/tests/integration/targets/mssql_script/aliases new file mode 100644 index 000000000..023e2edce --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mssql_script/aliases @@ -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 + +skip/osx +skip/macos +skip/freebsd +skip/rhel +disabled +destructive diff --git a/ansible_collections/community/general/tests/integration/targets/mssql_script/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/mssql_script/defaults/main.yml new file mode 100644 index 000000000..d1ca77f55 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mssql_script/defaults/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 + +mssql_host: localhost +mssql_port: 1433 +mssql_login_user: sa +mssql_login_password: "Abcd!234" diff --git a/ansible_collections/community/general/tests/integration/targets/mssql_script/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/mssql_script/meta/main.yml new file mode 100644 index 000000000..5769ff1cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mssql_script/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_docker 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 new file mode 100644 index 000000000..6fa4d3501 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml @@ -0,0 +1,246 @@ +--- +#################################################################### +# 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 + +# TODO: Find out how to setup mssql server for tests +# For the moment you have to run the tests locally +# docker run --name mssql-test -e "ACCEPT_EULA=Y" -e 'SA_PASSWORD={{ mssql_login_password }}' -p "{ mssql_port }"0:"{ mssql_port }" -d mcr.microsoft.com/mssql/server:2019-latest +# ansible-test integration mssql_script -v --allow-disabled + +- name: Install pymssql + ansible.builtin.pip: + name: + - pymssql + state: present + +- name: Start container + community.docker.docker_container: + name: mssql-test + image: "mcr.microsoft.com/mssql/server:2019-latest" + env: + ACCEPT_EULA: "Y" + SA_PASSWORD: "{{ mssql_login_password }}" + MSSQL_PID: Developer + ports: + - "{{ mssql_port }}:1433" + detach: true + auto_remove: true + memory: 2200M + +- name: Check default ports + ansible.builtin.wait_for: + host: "{{ mssql_host }}" + port: "{{ mssql_port }}" + state: started # Port should be open + delay: 10 # Wait 10 secs before first check + timeout: 30 # Stop checking after timeout (sec) + +- name: Check DB connection + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: "SELECT 1" + +- name: two batches with default output + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: | + SELECT 'Batch 0 - Select 0' + SELECT 'Batch 0 - Select 1' + GO + SELECT 'Batch 1 - Select 0' + register: result_batches +# "result_batches.query_results": +# [ # batches +# [ # selects +# [ # Rows +# [ # Columns +# "Batch 1 - Select 1" +# ] +# ], +# [ +# [ +# "Batch 1 - Select 2" +# ] +# ] +# ], +# [ +# [ +# [ +# "Batch 2 - Select 1" +# ] +# ] +# ] +# ] + +- assert: + that: + - result_batches.query_results | length == 2 # two batch results + - result_batches.query_results[0] | length == 2 # two selects in first batch + - result_batches.query_results[0][0] | length == 1 # one row in first select + - result_batches.query_results[0][0][0] | length == 1 # one column in first row + - result_batches.query_results[0][0][0][0] == 'Batch 0 - Select 0' # first column of first row + + +- name: two batches with dict output + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + output: dict + script: | + SELECT 'Batch 0 - Select 0' as b0s0 + SELECT 'Batch 0 - Select 1' as b0s1 + GO + SELECT 'Batch 1 - Select 0' as b1s0 + register: result_batches_dict +# "result_batches_dict.query_results": +# [ # batches +# [ # selects +# [ # Rows +# { # dict columns +# "b0s0": "Batch 0 - Select 0" +# } +# ], +# [ +# { +# "b0s1": "Batch 0 - Select 1" +# } +# ] +# ], +# [ +# [ +# { +# "b1s0": "Batch 1 - Select 0" +# } +# ] +# ] +# ] +- assert: + that: + - result_batches_dict.query_results_dict | length == 2 # two batch results + - result_batches_dict.query_results_dict[0] | length == 2 # two selects in first batch + - 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: Stored procedure may return multiple result sets + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: sp_spaceused + output: dict + register: result_spaceused +- assert: + that: + - result_spaceused.query_results_dict | length == 1 # one batch + - result_spaceused.query_results_dict[0] | length == 2 # stored procedure returns two result sets + - result_spaceused.query_results_dict[0][0][0]['database_name'] == 'master' # output dict + +- name: Ensure that passed 'db' is used + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: exec sp_spaceused + output: dict + db: msdb + register: result_db +- assert: + that: + - result_db.query_results_dict[0][0][0]['database_name'] == 'msdb' + +- name: pass params to query + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: | + SELECT name, state_desc FROM sys.databases WHERE name = %(dbname)s + params: + dbname: msdb + register: result_params +- assert: + that: + - result_params.query_results[0][0][0][0] == 'msdb' + - result_params.query_results[0][0][0][1] == 'ONLINE' + +- name: check_mode connects but does not run the query + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: SELECT Invalid_Column FROM Does_Not_Exist WITH Invalid Syntax + check_mode: true + register: check_mode +- assert: + that: check_mode.query_results is undefined + +- name: "Test: Value of unknown type: " + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: | + SELECT service_broker_guid, * FROM sys.databases WHERE name = 'master' + register: result_databases +- debug: + var: result_databases +- name: check types + assert: + that: + - result_databases.query_results[0][0][0][0] == '00000000-0000-0000-0000-000000000000' # guid + - result_databases.query_results[0][0][0][1] == 'master' # string + - result_databases.query_results[0][0][0][3] == None # byte string representation + - result_databases.query_results[0][0][0][4] == "b'\\x01'" # byte string representation + - result_databases.query_results[0][0][0][6] == 150 # int + - result_databases.query_results[0][0][0][10] == false # bool + +- name: "Test: Value of unknown type: -dict" + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + output: dict + script: | + SELECT service_broker_guid, * FROM sys.databases + +# Known issue: empty result set breaks return values +- name: empty result set + community.general.mssql_script: + login_user: "{{ mssql_login_user }}" + login_password: "{{ mssql_login_password }}" + login_host: "{{ mssql_host }}" + login_port: "{{ mssql_port }}" + script: | + SELECT name, state_desc FROM sys.databases WHERE name = %(dbname)s + SELECT name, state_desc FROM sys.databases WHERE name = 'DoesNotexist' + SELECT name, state_desc FROM sys.databases WHERE name = %(dbname)s + params: + dbname: msdb + register: empty_result +- assert: + that: + - empty_result.query_results[0] | length == 3 # == 1 ; issue: only first result is returned + - empty_result.query_results[0][0][0][0] == 'msdb' + - empty_result.query_results[0][1] | length == 0 + - empty_result.query_results[0][2][0][0] == 'msdb' + failed_when: false # known issue diff --git a/ansible_collections/community/general/tests/integration/targets/nomad/aliases b/ansible_collections/community/general/tests/integration/targets/nomad/aliases new file mode 100644 index 000000000..ad2435c82 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/nomad/aliases @@ -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 + +azp/posix/2 +nomad_job_info +destructive +skip/aix +skip/centos6 +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/nomad/files/job.hcl b/ansible_collections/community/general/tests/integration/targets/nomad/files/job.hcl new file mode 100644 index 000000000..8f01f0439 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/nomad/files/job.hcl @@ -0,0 +1,400 @@ +# 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 + +# There can only be a single job definition per file. This job is named +# "example" so it will create a job with the ID and Name "example". + +# The "job" stanza is the top-most configuration option in the job +# specification. A job is a declarative specification of tasks that Nomad +# should run. Jobs have a globally unique name, one or many task groups, which +# are themselves collections of one or many tasks. +# +# For more information and examples on the "job" stanza, please see +# the online documentation at: +# +# +# https://www.nomadproject.io/docs/job-specification/job.html +# +job "example" { + # The "region" parameter specifies the region in which to execute the job. + # If omitted, this inherits the default region name of "global". + # region = "global" + # + # The "datacenters" parameter specifies the list of datacenters which should + # be considered when placing this task. This must be provided. + datacenters = ["dc1"] + + # The "type" parameter controls the type of job, which impacts the scheduler's + # decision on placement. This configuration is optional and defaults to + # "service". For a full list of job types and their differences, please see + # the online documentation. + # + # For more information, please see the online documentation at: + # + # https://www.nomadproject.io/docs/jobspec/schedulers.html + # + type = "service" + + + # The "constraint" stanza defines additional constraints for placing this job, + # in addition to any resource or driver constraints. This stanza may be placed + # at the "job", "group", or "task" level, and supports variable interpolation. + # + # For more information and examples on the "constraint" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/constraint.html + # + # constraint { + # attribute = "${attr.kernel.name}" + # value = "linux" + # } + + # The "update" stanza specifies the update strategy of task groups. The update + # strategy is used to control things like rolling upgrades, canaries, and + # blue/green deployments. If omitted, no update strategy is enforced. The + # "update" stanza may be placed at the job or task group. When placed at the + # job, it applies to all groups within the job. When placed at both the job and + # group level, the stanzas are merged with the group's taking precedence. + # + # For more information and examples on the "update" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/update.html + # + update { + # The "max_parallel" parameter specifies the maximum number of updates to + # perform in parallel. In this case, this specifies to update a single task + # at a time. + max_parallel = 1 + + # The "min_healthy_time" parameter specifies the minimum time the allocation + # must be in the healthy state before it is marked as healthy and unblocks + # further allocations from being updated. + min_healthy_time = "10s" + + # The "healthy_deadline" parameter specifies the deadline in which the + # allocation must be marked as healthy after which the allocation is + # automatically transitioned to unhealthy. Transitioning to unhealthy will + # fail the deployment and potentially roll back the job if "auto_revert" is + # set to true. + healthy_deadline = "3m" + + # The "progress_deadline" parameter specifies the deadline in which an + # allocation must be marked as healthy. The deadline begins when the first + # allocation for the deployment is created and is reset whenever an allocation + # as part of the deployment transitions to a healthy state. If no allocation + # transitions to the healthy state before the progress deadline, the + # deployment is marked as failed. + progress_deadline = "10m" + + # The "auto_revert" parameter specifies if the job should auto-revert to the + # last stable job on deployment failure. A job is marked as stable if all the + # allocations as part of its deployment were marked healthy. + auto_revert = false + + # The "canary" parameter specifies that changes to the job that would result + # in destructive updates should create the specified number of canaries + # without stopping any previous allocations. Once the operator determines the + # canaries are healthy, they can be promoted which unblocks a rolling update + # of the remaining allocations at a rate of "max_parallel". + # + # Further, setting "canary" equal to the count of the task group allows + # blue/green deployments. When the job is updated, a full set of the new + # version is deployed and upon promotion the old version is stopped. + canary = 0 + } + # The migrate stanza specifies the group's strategy for migrating off of + # draining nodes. If omitted, a default migration strategy is applied. + # + # For more information on the "migrate" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/migrate.html + # + migrate { + # Specifies the number of task groups that can be migrated at the same + # time. This number must be less than the total count for the group as + # (count - max_parallel) will be left running during migrations. + max_parallel = 1 + + # Specifies the mechanism in which allocations health is determined. The + # potential values are "checks" or "task_states". + health_check = "checks" + + # Specifies the minimum time the allocation must be in the healthy state + # before it is marked as healthy and unblocks further allocations from being + # migrated. This is specified using a label suffix like "30s" or "15m". + min_healthy_time = "10s" + + # Specifies the deadline in which the allocation must be marked as healthy + # after which the allocation is automatically transitioned to unhealthy. This + # is specified using a label suffix like "2m" or "1h". + healthy_deadline = "5m" + } + # The "group" stanza defines a series of tasks that should be co-located on + # the same Nomad client. Any task within a group will be placed on the same + # client. + # + # For more information and examples on the "group" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/group.html + # + group "cache" { + # The "count" parameter specifies the number of the task groups that should + # be running under this group. This value must be non-negative and defaults + # to 1. + count = 1 + + # The "restart" stanza configures a group's behavior on task failure. If + # left unspecified, a default restart policy is used based on the job type. + # + # For more information and examples on the "restart" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/restart.html + # + restart { + # The number of attempts to run the job within the specified interval. + attempts = 2 + interval = "30m" + + # The "delay" parameter specifies the duration to wait before restarting + # a task after it has failed. + delay = "15s" + + # The "mode" parameter controls what happens when a task has restarted + # "attempts" times within the interval. "delay" mode delays the next + # restart until the next interval. "fail" mode does not restart the task + # if "attempts" has been hit within the interval. + mode = "fail" + } + + # The "ephemeral_disk" stanza instructs Nomad to utilize an ephemeral disk + # instead of a hard disk requirement. Clients using this stanza should + # not specify disk requirements in the resources stanza of the task. All + # tasks in this group will share the same ephemeral disk. + # + # For more information and examples on the "ephemeral_disk" stanza, please + # see the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/ephemeral_disk.html + # + ephemeral_disk { + # When sticky is true and the task group is updated, the scheduler + # will prefer to place the updated allocation on the same node and + # will migrate the data. This is useful for tasks that store data + # that should persist across allocation updates. + # sticky = true + # + # Setting migrate to true results in the allocation directory of a + # sticky allocation directory to be migrated. + # migrate = true + # + # The "size" parameter specifies the size in MB of shared ephemeral disk + # between tasks in the group. + size = 300 + } + + # The "affinity" stanza enables operators to express placement preferences + # based on node attributes or metadata. + # + # For more information and examples on the "affinity" stanza, please + # see the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/affinity.html + # + # affinity { + # attribute specifies the name of a node attribute or metadata + # attribute = "${node.datacenter}" + + + # value specifies the desired attribute value. In this example Nomad + # will prefer placement in the "us-west1" datacenter. + # value = "us-west1" + + + # weight can be used to indicate relative preference + # when the job has more than one affinity. It defaults to 50 if not set. + # weight = 100 + # } + + + # The "spread" stanza allows operators to increase the failure tolerance of + # their applications by specifying a node attribute that allocations + # should be spread over. + # + # For more information and examples on the "spread" stanza, please + # see the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/spread.html + # + # spread { + # attribute specifies the name of a node attribute or metadata + # attribute = "${node.datacenter}" + + + # targets can be used to define desired percentages of allocations + # for each targeted attribute value. + # + # target "us-east1" { + # percent = 60 + # } + # target "us-west1" { + # percent = 40 + # } + # } + + # The "task" stanza creates an individual unit of work, such as a Docker + # container, web application, or batch processing. + # + # For more information and examples on the "task" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/task.html + # + task "redis" { + # The "driver" parameter specifies the task driver that should be used to + # run the task. + driver = "docker" + + # The "config" stanza specifies the driver configuration, which is passed + # directly to the driver to start the task. The details of configurations + # are specific to each driver, so please see specific driver + # documentation for more information. + config { + image = "redis:3.2" + + port_map { + db = 6379 + } + } + + # The "artifact" stanza instructs Nomad to download an artifact from a + # remote source prior to starting the task. This provides a convenient + # mechanism for downloading configuration files or data needed to run the + # task. It is possible to specify the "artifact" stanza multiple times to + # download multiple artifacts. + # + # For more information and examples on the "artifact" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/artifact.html + # + # artifact { + # source = "http://foo.com/artifact.tar.gz" + # options { + # checksum = "md5:c4aa853ad2215426eb7d70a21922e794" + # } + # } + + + # The "logs" stanza instructs the Nomad client on how many log files and + # the maximum size of those logs files to retain. Logging is enabled by + # default, but the "logs" stanza allows for finer-grained control over + # the log rotation and storage configuration. + # + # For more information and examples on the "logs" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/logs.html + # + # logs { + # max_files = 10 + # max_file_size = 15 + # } + + # The "resources" stanza describes the requirements a task needs to + # execute. Resource requirements include memory, network, cpu, and more. + # This ensures the task will execute on a machine that contains enough + # resource capacity. + # + # For more information and examples on the "resources" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/resources.html + # + resources { + cpu = 500 # 500 MHz + memory = 256 # 256MB + + network { + mbits = 10 + port "db" {} + } + } + # The "service" stanza instructs Nomad to register this task as a service + # in the service discovery engine, which is currently Consul. This will + # make the service addressable after Nomad has placed it on a host and + # port. + # + # For more information and examples on the "service" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/service.html + # + service { + name = "redis-cache" + tags = ["global", "cache"] + port = "db" + + check { + name = "alive" + type = "tcp" + interval = "10s" + timeout = "2s" + } + } + + # The "template" stanza instructs Nomad to manage a template, such as + # a configuration file or script. This template can optionally pull data + # from Consul or Vault to populate runtime configuration data. + # + # For more information and examples on the "template" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/template.html + # + # template { + # data = "---\nkey: {{ key \"service/my-key\" }}" + # destination = "local/file.yml" + # change_mode = "signal" + # change_signal = "SIGHUP" + # } + + # The "template" stanza can also be used to create environment variables + # for tasks that prefer those to config files. The task will be restarted + # when data pulled from Consul or Vault changes. + # + # template { + # data = "KEY={{ key \"service/my-key\" }}" + # destination = "local/file.env" + # env = true + # } + + # The "vault" stanza instructs the Nomad client to acquire a token from + # a HashiCorp Vault server. The Nomad servers must be configured and + # authorized to communicate with Vault. By default, Nomad will inject + # The token into the job via an environment variable and make the token + # available to the "template" stanza. The Nomad client handles the renewal + # and revocation of the Vault token. + # + # For more information and examples on the "vault" stanza, please see + # the online documentation at: + # + # https://www.nomadproject.io/docs/job-specification/vault.html + # + # vault { + # policies = ["cdn", "frontend"] + # change_mode = "signal" + # change_signal = "SIGHUP" + # } + + # Controls the timeout between signalling a task it will be killed + # and killing the task. If not set a default is used. + # kill_timeout = "20s" + } + } +} diff --git a/ansible_collections/community/general/tests/integration/targets/nomad/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/nomad/meta/main.yml new file mode 100644 index 000000000..0909be206 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/nomad/meta/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 + +dependencies: + - setup_pkg_mgr + - setup_openssl + - setup_remote_tmp_dir + - setup_remote_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/nomad/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/nomad/tasks/main.yml new file mode 100644 index 000000000..1a143be05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/nomad/tasks/main.yml @@ -0,0 +1,111 @@ +--- +# 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: Skip unsupported platforms + meta: end_play + # TODO: figure out why Alpine does not work! + when: | + ansible_distribution == 'CentOS' and ansible_distribution_major_version is not version('7', '>=') + or ansible_distribution == 'Alpine' + +- name: Install Nomad and test + vars: + nomad_version: 0.12.4 + nomad_uri: https://releases.hashicorp.com/nomad/{{ nomad_version }}/nomad_{{ nomad_version }}_{{ ansible_system | lower }}_{{ nomad_arch }}.zip + nomad_cmd: '{{ remote_tmp_dir }}/nomad' + block: + + - name: Install requests<2.20 (CentOS/RHEL 6) + pip: + name: requests<2.20 + extra_args: "-c {{ remote_constraints }}" + register: result + until: result is success + when: ansible_distribution_file_variety|default() == 'RedHat' and ansible_distribution_major_version is version('6', '<=') + + - name: Install python-nomad + pip: + name: python-nomad + extra_args: "-c {{ remote_constraints }}" + register: result + until: result is success + + - name: Install jmespath + pip: + name: jmespath + extra_args: "-c {{ remote_constraints }}" + register: result + until: result is success + + - name: Generate privatekey + community.crypto.openssl_privatekey: + path: '{{ remote_tmp_dir }}/privatekey.pem' + + - name: Generate CSR + community.crypto.openssl_csr: + path: '{{ remote_tmp_dir }}/csr.csr' + privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem' + subject: + commonName: localhost + + - name: Generate selfsigned certificate + register: selfsigned_certificate + community.crypto.x509_certificate: + path: '{{ remote_tmp_dir }}/cert.pem' + csr_path: '{{ remote_tmp_dir }}/csr.csr' + privatekey_path: '{{ remote_tmp_dir }}/privatekey.pem' + provider: selfsigned + selfsigned_digest: sha256 + + - name: Install unzip + package: + name: unzip + register: result + until: result is success + when: ansible_distribution != "MacOSX" + + - assert: + that: ansible_architecture in ['i386', 'x86_64', 'amd64'] + + - set_fact: + nomad_arch: '386' + when: ansible_architecture == 'i386' + + - set_fact: + nomad_arch: amd64 + when: ansible_architecture in ['x86_64', 'amd64'] + + - name: Download nomad binary + unarchive: + src: '{{ nomad_uri }}' + dest: '{{ remote_tmp_dir }}' + remote_src: true + register: result + until: result is success + + - vars: + remote_dir: '{{ echo_remote_tmp_dir.stdout }}' + block: + + - command: echo {{ remote_tmp_dir }} + register: echo_remote_tmp_dir + + - name: Run tests integration + block: + - name: Start nomad (dev mode enabled) + shell: nohup {{ nomad_cmd }} agent -dev /dev/null 2>&1 & + + - name: wait nomad up + wait_for: + host: localhost + port: 4646 + delay: 10 + timeout: 60 + + - import_tasks: nomad_job.yml + always: + + - name: kill nomad + shell: pkill nomad diff --git a/ansible_collections/community/general/tests/integration/targets/nomad/tasks/nomad_job.yml b/ansible_collections/community/general/tests/integration/targets/nomad/tasks/nomad_job.yml new file mode 100644 index 000000000..2a4f223aa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/nomad/tasks/nomad_job.yml @@ -0,0 +1,111 @@ +--- +# 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: run check deploy nomad job + nomad_job: + host: localhost + state: present + use_ssl: false + content: "{{ lookup('file', 'job.hcl') }}" + register: job_check_deployed + check_mode: true + +- name: run create nomad job + nomad_job: + host: localhost + state: present + use_ssl: false + content: "{{ lookup('file', 'job.hcl') }}" + force_start: true + register: job_deployed + +- name: get nomad job deployed + nomad_job_info: + host: localhost + use_ssl: false + name: example + register: get_nomad_job + +- name: get list of nomad jobs + nomad_job_info: + host: localhost + use_ssl: false + register: list_nomad_jobs + +- name: assert job is deployed and tasks is changed + assert: + that: + - job_check_deployed is changed + - job_deployed is changed + - get_nomad_job.result[0].ID == "example" + - list_nomad_jobs.result | length == 1 + +- name: run check deploy job idempotence + nomad_job: + host: localhost + state: present + use_ssl: false + content: "{{ lookup('file', 'job.hcl') }}" + register: job_check_deployed_idempotence + check_mode: true + +- name: run create nomad job idempotence + nomad_job: + host: localhost + state: present + use_ssl: false + content: "{{ lookup('file', 'job.hcl') }}" + register: job_deployed_idempotence + +- name: get list of nomad jobs + nomad_job_info: + host: localhost + use_ssl: false + register: list_nomad_jobs + +- debug: + msg: "{{ list_nomad_jobs }}" + +- name: run check delete nomad job + nomad_job: + host: localhost + state: absent + use_ssl: false + content: "{{ lookup('file', 'job.hcl') }}" + register: job_deleted_check + check_mode: true + +- name: run delete nomad job + nomad_job: + host: localhost + state: absent + use_ssl: false + content: "{{ lookup('file', 'job.hcl') }}" + register: job_deleted + +- name: get job deleted + nomad_job_info: + host: localhost + use_ssl: false + name: example + register: get_job_delete + +- name: get list of nomad jobs + nomad_job_info: + host: localhost + use_ssl: false + register: list_nomad_jobs + +- debug: + msg: "{{ list_nomad_jobs }}" + +- name: assert idempotence + assert: + that: + - job_check_deployed_idempotence is not changed + - job_deployed_idempotence is not changed + - job_deleted_check is changed + - job_deleted is changed + - get_job_delete.result[0].Stop diff --git a/ansible_collections/community/general/tests/integration/targets/npm/aliases b/ansible_collections/community/general/tests/integration/targets/npm/aliases new file mode 100644 index 000000000..6e2c65f38 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/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/aix +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/npm/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/npm/meta/main.yml new file mode 100644 index 000000000..6147ad33e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/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/npm/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/npm/tasks/main.yml new file mode 100644 index 000000000..500e15fdb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/tasks/main.yml @@ -0,0 +1,32 @@ +--- +#################################################################### +# 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 npm 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 + +# ------------------------------------------------------------- +# Setup steps + +- when: + - not (ansible_os_family == 'Alpine') # TODO + block: + + # expand remote path + - command: 'echo {{ remote_tmp_dir }}' + register: echo + - set_fact: + remote_dir: '{{ echo.stdout }}' + + - include_tasks: run.yml + vars: + nodejs_version: '{{ item }}' + nodejs_path: 'node-v{{ nodejs_version }}-{{ ansible_system|lower }}-x{{ ansible_userspace_bits }}' + with_items: + - 7.10.1 # provides npm 4.2.0 (last npm < 5 released) + - 8.0.0 # provides npm 5.0.0 + - 8.2.0 # provides npm 5.3.0 (output change with this version) diff --git a/ansible_collections/community/general/tests/integration/targets/npm/tasks/no_bin_links.yml b/ansible_collections/community/general/tests/integration/targets/npm/tasks/no_bin_links.yml new file mode 100644 index 000000000..3588f7642 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/tasks/no_bin_links.yml @@ -0,0 +1,68 @@ +--- +# 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 any node modules' + file: + path: '{{ remote_dir }}/node_modules' + state: absent + +- vars: + # sample: node-v8.2.0-linux-x64.tar.xz + node_path: '{{ remote_dir }}/{{ nodejs_path }}/bin' + package: 'ncp' + block: + - shell: npm --version + environment: + PATH: '{{ node_path }}:{{ ansible_env.PATH }}' + register: npm_version + + - debug: + var: npm_version.stdout + + - name: 'Install simple package with no_bin_links disabled' + npm: + path: '{{ remote_dir }}' + executable: '{{ node_path }}/npm' + state: present + name: '{{ package }}' + no_bin_links: false + environment: + PATH: '{{ node_path }}:{{ ansible_env.PATH }}' + register: npm_install_no_bin_links_disabled + + - name: 'Make sure .bin folder has been created' + stat: + path: "{{ remote_dir }}/node_modules/.bin" + register: npm_dotbin_folder_disabled + + - name: 'Remove any node modules' + file: + path: '{{ remote_dir }}/node_modules' + state: absent + + - name: 'Install simple package with no_bin_links enabled' + npm: + path: '{{ remote_dir }}' + executable: '{{ node_path }}/npm' + state: present + name: '{{ package }}' + no_bin_links: true + environment: + PATH: '{{ node_path }}:{{ ansible_env.PATH }}' + register: npm_install_no_bin_links_enabled + + - name: 'Make sure .bin folder has not been created' + stat: + path: "{{ remote_dir }}/node_modules/.bin" + register: npm_dotbin_folder_enabled + + - assert: + that: + - npm_install_no_bin_links_disabled is success + - npm_install_no_bin_links_disabled is changed + - npm_install_no_bin_links_enabled is success + - npm_install_no_bin_links_enabled is changed + - npm_dotbin_folder_disabled.stat.exists + - not npm_dotbin_folder_enabled.stat.exists diff --git a/ansible_collections/community/general/tests/integration/targets/npm/tasks/run.yml b/ansible_collections/community/general/tests/integration/targets/npm/tasks/run.yml new file mode 100644 index 000000000..9ce380270 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/tasks/run.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 + +- include_tasks: setup.yml +- include_tasks: test.yml +- include_tasks: no_bin_links.yml diff --git a/ansible_collections/community/general/tests/integration/targets/npm/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/npm/tasks/setup.yml new file mode 100644 index 000000000..bad927915 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/tasks/setup.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 + +- name: 'Download NPM' + unarchive: + src: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/npm/{{ nodejs_path }}.tar.gz' + dest: '{{ remote_tmp_dir }}' + remote_src: true + creates: '{{ remote_tmp_dir }}/{{ nodejs_path }}.tar.gz' 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 new file mode 100644 index 000000000..c8e83f602 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml @@ -0,0 +1,74 @@ +--- +# 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 any node modules' + file: + path: '{{ remote_dir }}/node_modules' + state: absent + +- vars: + # sample: node-v8.2.0-linux-x64.tar.xz + node_path: '{{ remote_dir }}/{{ nodejs_path }}/bin' + package: 'iconv-lite' + block: + - shell: npm --version + environment: + PATH: '{{ node_path }}:{{ ansible_env.PATH }}' + register: npm_version + + - debug: + var: npm_version.stdout + + - 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: + that: + - npm_install is success + - npm_install is changed + + - 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 + assert: + that: + - npm_reinstall is success + - not (npm_reinstall is changed) + + - name: 'Manually delete package' + file: + path: '{{ remote_dir }}/node_modules/{{ package }}' + state: absent + + - 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 + assert: + that: + - npm_fix_install is success + - npm_fix_install is changed diff --git a/ansible_collections/community/general/tests/integration/targets/odbc/aliases b/ansible_collections/community/general/tests/integration/targets/odbc/aliases new file mode 100644 index 000000000..e8465c50e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/odbc/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 +destructive +skip/osx +skip/macos +skip/rhel8.0 +skip/rhel9.0 +skip/rhel9.1 +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/odbc/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/odbc/defaults/main.yml new file mode 100644 index 000000000..dd75f5471 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/odbc/defaults/main.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 + +# defaults file for test_postgresql_db +my_user: 'ansible_user' +my_pass: 'md5d5e044ccd9b4b8adc89e8fed2eb0db8a' +my_pass_decrypted: '6EjMk +# 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: Check if name is required for present + osx_defaults: + domain: NSGlobalDomain + key: AppleMeasurementUnits + type: string + state: present + register: missing_value + ignore_errors: true + +- name: Test if state and value are required together + assert: + that: + - "'following are missing: value' in '{{ missing_value['msg'] }}'" + +- name: Change value of AppleMeasurementUnits to centimeter in check_mode + osx_defaults: + domain: NSGlobalDomain + key: AppleMeasurementUnits + type: string + value: Centimeter + state: present + register: measure_task_check_mode + check_mode: true + +- name: Test if AppleMeasurementUnits value is changed to Centimeters in check_mode + assert: + that: + - measure_task_check_mode.changed + +- name: Find the current value of AppleMeasurementUnits + osx_defaults: + domain: NSGlobalDomain + key: AppleMeasurementUnits + state: list + register: apple_measure_value + +- debug: + msg: "{{ apple_measure_value['value'] }}" + +- set_fact: + new_value: "Centimeters" + when: apple_measure_value['value'] == 'Inches' or apple_measure_value['value'] == None + +- set_fact: + new_value: "Inches" + when: apple_measure_value['value'] == 'Centimeters' + +- name: Change value of AppleMeasurementUnits to {{ new_value }} + osx_defaults: + domain: NSGlobalDomain + key: AppleMeasurementUnits + type: string + value: "{{ new_value }}" + state: present + register: change_value + +- name: Test if AppleMeasurementUnits value is changed to {{ new_value }} + assert: + that: + - change_value.changed + +- name: Again change value of AppleMeasurementUnits to {{ new_value }} + osx_defaults: + domain: NSGlobalDomain + key: AppleMeasurementUnits + type: string + value: "{{ new_value }}" + state: present + register: change_value + +- name: Again test if AppleMeasurementUnits value is not changed to {{ new_value }} + assert: + that: + - not change_value.changed + +- name: Check a fake setting for delete operation + osx_defaults: + domain: com.ansible.fake_value + key: ExampleKeyToRemove + state: list + register: list_fake_value + +- debug: + msg: "{{ list_fake_value }}" + +- name: Check if fake value is listed + assert: + that: + - not list_fake_value.changed + +- name: Create a fake setting for delete operation + osx_defaults: + domain: com.ansible.fake_value + key: ExampleKeyToRemove + state: present + value: sample + register: present_fake_value + +- debug: + msg: "{{ present_fake_value }}" + +- name: Check if fake is created + assert: + that: + - present_fake_value.changed + when: present_fake_value.changed + +- name: List a fake setting + osx_defaults: + domain: com.ansible.fake_value + key: ExampleKeyToRemove + state: list + register: list_fake + +- debug: + msg: "{{ list_fake }}" + +- name: Delete a fake setting + osx_defaults: + domain: com.ansible.fake_value + key: ExampleKeyToRemove + state: absent + register: absent_task + +- debug: + msg: "{{ absent_task }}" + +- name: Check if fake setting is deleted + assert: + that: + - absent_task.changed + when: present_fake_value.changed + +- name: Try deleting a fake setting again + osx_defaults: + domain: com.ansible.fake_value + key: ExampleKeyToRemove + state: absent + register: absent_task + +- debug: + msg: "{{ absent_task }}" + +- name: Check if fake setting is not deleted + assert: + that: + - not absent_task.changed + +- name: Delete operation in check_mode + osx_defaults: + domain: com.ansible.fake_value + key: ExampleKeyToRemove + state: absent + register: absent_check_mode_task + check_mode: true + +- debug: + msg: "{{ absent_check_mode_task }}" + +- name: Check delete operation with check mode + assert: + that: + - not absent_check_mode_task.changed + + +- name: Use different data types and check if it works with them + osx_defaults: + domain: com.ansible.fake_values + key: "{{ item.key }}" + type: "{{ item.type }}" + value: "{{ item.value }}" + state: present + with_items: &data_type + - { type: 'int', value: 1, key: 'sample_int'} + - { type: 'integer', value: 1, key: 'sample_int_2'} + - { type: 'integer', value: -1, key: 'negative_int'} + - { type: 'bool', value: true, key: 'sample_bool'} + - { type: 'boolean', value: true, key: 'sample_bool_2'} + - { type: 'date', value: "2019-02-19 10:10:10", key: 'sample_date'} + - { type: 'float', value: 1.2, key: 'sample_float'} + - { type: 'string', value: 'sample', key: 'sample_string'} + - { type: 'array', value: ['1', '2'], key: 'sample_array'} + register: test_data_types + +- assert: + that: "{{ item.changed }}" + with_items: "{{ test_data_types.results }}" + +- name: Use different data types and delete them + osx_defaults: + domain: com.ansible.fake_values + key: "{{ item.key }}" + value: "{{ item.value }}" + type: "{{ item.type }}" + state: absent + with_items: *data_type + register: test_data_types + +- assert: + that: "{{ item.changed }}" + with_items: "{{ test_data_types.results }}" + + +- name: Ensure test key does not exist + osx_defaults: + domain: com.ansible.fake_array_value + key: ExampleArrayKey + state: absent + +- name: add array value for the first time + osx_defaults: + domain: com.ansible.fake_array_value + key: ExampleArrayKey + value: + - 'Value with spaces' + type: array + array_add: true + register: test_array_add + +- assert: + that: test_array_add.changed + +- name: add for the second time, should be skipped + osx_defaults: + domain: com.ansible.fake_array_value + key: ExampleArrayKey + value: + - 'Value with spaces' + type: array + array_add: true + register: test_array_add + +- assert: + that: not test_array_add.changed + +- name: Clean up test key + osx_defaults: + domain: com.ansible.fake_array_value + key: ExampleArrayKey + state: absent + register: test_array_add + +- assert: + that: test_array_add.changed diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/aliases b/ansible_collections/community/general/tests/integration/targets/pacman/aliases new file mode 100644 index 000000000..1d25c0193 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/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 +destructive +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/pacman/meta/main.yml new file mode 100644 index 000000000..08ce20c21 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/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_remote_tmp_dir + - setup_pkg_mgr diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/basic.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/basic.yml new file mode 100644 index 000000000..ae2f9c0b5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/basic.yml @@ -0,0 +1,86 @@ +--- +# 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 + +- vars: + package_name: unarj + block: + - name: Make sure that {{ package_name }} is not installed + pacman: + name: '{{ package_name }}' + state: absent + + - name: Install {{ package_name }} (check mode) + pacman: + name: '{{ package_name }}' + state: present + check_mode: true + register: install_1 + + - name: Install {{ package_name }} + pacman: + name: '{{ package_name }}' + state: present + register: install_2 + + - name: Install {{ package_name }} (check mode, idempotent) + pacman: + name: '{{ package_name }}' + state: present + check_mode: true + register: install_3 + + - name: Install {{ package_name }} (idempotent) + pacman: + name: '{{ package_name }}' + state: present + register: install_4 + + - assert: + that: + - install_1 is changed + - install_1.msg == 'Would have installed 1 packages' + - install_2 is changed + - install_2.msg == 'Installed 1 package(s)' + - install_3 is not changed + - install_3.msg == 'package(s) already installed' + - install_4 is not changed + - install_4.msg == 'package(s) already installed' + + - name: Uninstall {{ package_name }} (check mode) + pacman: + name: '{{ package_name }}' + state: absent + check_mode: true + register: uninstall_1 + + - name: Uninstall {{ package_name }} + pacman: + name: '{{ package_name }}' + state: absent + register: uninstall_2 + + - name: Uninstall {{ package_name }} (check mode, idempotent) + pacman: + name: '{{ package_name }}' + state: absent + check_mode: true + register: uninstall_3 + + - name: Uninstall {{ package_name }} (idempotent) + pacman: + name: '{{ package_name }}' + state: absent + register: uninstall_4 + + - assert: + that: + - uninstall_1 is changed + - uninstall_1.msg == 'Would have removed 1 packages' + - uninstall_2 is changed + - uninstall_2.msg == 'Removed 1 package(s)' + - uninstall_3 is not changed + - uninstall_3.msg == 'package(s) already absent' + - uninstall_4 is not changed + - uninstall_4.msg == 'package(s) already 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 new file mode 100644 index 000000000..a5f183236 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml @@ -0,0 +1,85 @@ +--- +# 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 + +- vars: + package_name: ansible-test-foo + username: ansible-regular-user + block: + - name: Install fakeroot + pacman: + state: present + name: + - fakeroot + + - name: Create user + user: + name: '{{ username }}' + home: '/home/{{ username }}' + create_home: true + + - name: Create directory + file: + path: '/home/{{ username }}/{{ package_name }}' + state: directory + owner: '{{ username }}' + + - name: Create PKGBUILD + copy: + dest: '/home/{{ username }}/{{ package_name }}/PKGBUILD' + content: | + pkgname=('{{ package_name }}') + pkgver=1.0.0 + pkgrel=1 + pkgdesc="Test removing a local package not in the repositories" + arch=('any') + license=('GPL v3+') + owner: '{{ username }}' + + - name: Build package + command: + cmd: su {{ username }} -c "makepkg -srf" + chdir: '/home/{{ username }}/{{ package_name }}' + + - name: Install package + pacman: + state: present + name: + - '/home/{{ username }}/{{ package_name }}/{{ package_name }}-1.0.0-1-any.pkg.tar.zst' + + - name: Remove package (check mode) + pacman: + state: absent + name: + - '{{ package_name }}' + check_mode: true + register: remove_1 + + - name: Remove package + pacman: + state: absent + name: + - '{{ package_name }}' + register: remove_2 + + - name: Remove package (idempotent) + pacman: + state: absent + name: + - '{{ package_name }}' + register: remove_3 + + - name: Check conditions + assert: + that: + - remove_1 is changed + - remove_2 is changed + - remove_3 is not changed + + always: + - name: Remove directory + file: + path: '{{ remote_tmp_dir }}/{{ package_name }}' + state: absent + become: true 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 new file mode 100644 index 000000000..12d28a2d3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml @@ -0,0 +1,19 @@ +--- +#################################################################### +# 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 + +- when: ansible_os_family == 'Archlinux' + block: + # Add more tests here by including more task files: + - include_tasks: 'basic.yml' + - include_tasks: 'package_urls.yml' + - include_tasks: 'remove_nosave.yml' + - include_tasks: 'update_cache.yml' + - include_tasks: 'locally_installed_package.yml' + - include_tasks: 'reason.yml' diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/package_urls.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/package_urls.yml new file mode 100644 index 000000000..4df531285 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/package_urls.yml @@ -0,0 +1,219 @@ +--- +# 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 + +- vars: + http_port: 27617 + reg_pkg: ed + url_pkg: lemon + url_pkg_filename: url.pkg.zst + url_pkg_path: '/tmp/' + url_pkg_url: 'http://localhost:{{http_port}}/{{url_pkg_filename}}' + file_pkg: hdparm + file_pkg_path: /tmp/file.pkg.zst + extra_pkg: core/sdparm + extra_pkg_outfmt: sdparm + block: + - name: Make sure that test packages are not installed + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg}}' + - '{{file_pkg}}' + - '{{extra_pkg}}' + state: absent + - name: Make sure that url package is not cached + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Get URL for {{url_pkg}} + command: + cmd: pacman --sync --print-format "%l" {{url_pkg}} + register: url_pkg_stdout + - name: Download {{url_pkg}} pkg + get_url: + url: '{{url_pkg_stdout.stdout}}' + dest: '{{url_pkg_path}}/{{url_pkg_filename}}' + - name: Download {{url_pkg}} pkg sig + get_url: + url: '{{url_pkg_stdout.stdout}}.sig' + dest: '{{url_pkg_path}}/{{url_pkg_filename}}.sig' + - name: Host {{url_pkg}} + shell: + cmd: 'python -m http.server --directory {{url_pkg_path}} {{http_port}} >/dev/null 2>&1' + async: 90 + poll: 0 + - name: Wait for http.server to come up online + wait_for: + host: 'localhost' + port: '{{http_port}}' + state: started + + - name: Get URL for {{file_pkg}} + command: + cmd: pacman --sync --print-format "%l" {{file_pkg}} + register: file_pkg_url + - name: Download {{file_pkg}} pkg + get_url: + url: '{{file_pkg_url.stdout}}' + dest: '{{file_pkg_path}}' + + - name: Install packages from mixed sources (check mode) + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + check_mode: true + register: install_1 + + - name: Install packages from url (check mode, cached) + pacman: + name: + - '{{url_pkg_url}}' + check_mode: true + register: install_1c + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Install packages from mixed sources + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + register: install_2 + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Install packages from mixed sources - (idempotency) + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + register: install_3 + - name: Install packages from url - (idempotency, cached) + pacman: + name: + - '{{url_pkg_url}}' + register: install_3c + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Install packages with their regular names (idempotency) + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg}}' + - '{{file_pkg}}' + register: install_4 + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Install new package with already installed packages from mixed sources + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + - '{{extra_pkg}}' + register: install_5 + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Uninstall packages - mixed sources (check mode) + pacman: + state: absent + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + check_mode: true + register: uninstall_1 + - name: Uninstall packages - url (check mode, cached) + pacman: + state: absent + name: + - '{{url_pkg_url}}' + check_mode: true + register: uninstall_1c + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Uninstall packages - mixed sources + pacman: + state: absent + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + register: uninstall_2 + - name: Delete cached {{url_pkg}} + file: + path: '/var/cache/pacman/pkg/{{url_pkg_filename}}' + state: absent + + - name: Uninstall packages - mixed sources (idempotency) + pacman: + state: absent + name: + - '{{reg_pkg}}' + - '{{url_pkg_url}}' + - '{{file_pkg_path}}' + register: uninstall_3 + + - name: Uninstall package - url (idempotency, cached) + pacman: + state: absent + name: + - '{{url_pkg_url}}' + register: uninstall_3c + + - assert: + that: + - install_1 is changed + - install_1.msg == 'Would have installed 3 packages' + - install_1.packages|sort() == [reg_pkg, url_pkg, file_pkg]|sort() + - install_1c is changed + - install_1c.msg == 'Would have installed 1 packages' + - install_1c.packages|sort() == [url_pkg] + - install_2 is changed + - install_2.msg == 'Installed 3 package(s)' + - install_2.packages|sort() == [reg_pkg, url_pkg, file_pkg]|sort() + - install_3 is not changed + - install_3.msg == 'package(s) already installed' + - install_3c is not changed + - install_3c.msg == 'package(s) already installed' + - install_4 is not changed + - install_4.msg == 'package(s) already installed' + - install_5 is changed + - install_5.msg == 'Installed 1 package(s)' + - install_5.packages == [extra_pkg_outfmt] + - uninstall_1 is changed + - uninstall_1.msg == 'Would have removed 3 packages' + - uninstall_1.packages | length() == 3 # pkgs have versions here + - uninstall_1c is changed + - uninstall_1c.msg == 'Would have removed 1 packages' + - uninstall_1c.packages | length() == 1 # pkgs have versions here + - uninstall_2 is changed + - uninstall_2.msg == 'Removed 3 package(s)' + - uninstall_2.packages | length() == 3 + - uninstall_3 is not changed + - uninstall_3.msg == 'package(s) already absent' + - uninstall_3c is not changed + - uninstall_3c.msg == 'package(s) already absent' diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/reason.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/reason.yml new file mode 100644 index 000000000..5a26e3e10 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/reason.yml @@ -0,0 +1,101 @@ +--- +# 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 + +- vars: + reg_pkg: ed + url_pkg: lemon + file_pkg: hdparm + file_pkg_path: /tmp/pkg.zst + extra_pkg: core/sdparm + extra_pkg_outfmt: sdparm + block: + - name: Make sure that test packages are not installed + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg}}' + - '{{file_pkg}}' + - '{{extra_pkg}}' + state: absent + + - name: Get URL for {{url_pkg}} + command: + cmd: pacman --sync --print-format "%l" {{url_pkg}} + register: url_pkg_url + + - name: Get URL for {{file_pkg}} + command: + cmd: pacman --sync --print-format "%l" {{file_pkg}} + register: file_pkg_url + - name: Download {{file_pkg}} pkg + get_url: + url: '{{file_pkg_url.stdout}}' + dest: '{{file_pkg_path}}' + + - name: Install packages from mixed sources as dependency (check mode) + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url.stdout}}' + - '{{file_pkg_path}}' + reason: dependency + check_mode: true + register: install_1 + + - name: Install packages from mixed sources as explicit + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url.stdout}}' + - '{{file_pkg_path}}' + reason: explicit + register: install_2 + + - name: Install packages from mixed sources with new packages being installed as dependency - (idempotency) + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url.stdout}}' + - '{{file_pkg_path}}' + reason: dependency + register: install_3 + + - name: Install new package with already installed packages from mixed sources as dependency + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url.stdout}}' + - '{{file_pkg_path}}' + - '{{extra_pkg}}' + reason: dependency + register: install_4 + + - name: Set install reason for all packages to dependency + pacman: + name: + - '{{reg_pkg}}' + - '{{url_pkg_url.stdout}}' + - '{{file_pkg_path}}' + - '{{extra_pkg}}' + reason: dependency + reason_for: all + register: install_5 + + - assert: + that: + - install_1 is changed + - install_1.msg == 'Would have installed 3 packages' + - install_1.packages|sort() == [reg_pkg, url_pkg, file_pkg]|sort() + - install_2 is changed + - install_2.msg == 'Installed 3 package(s)' + - install_2.packages|sort() == [reg_pkg, url_pkg, file_pkg]|sort() + - install_3 is not changed + - install_3.msg == 'package(s) already installed' + - install_4 is changed + - install_4.msg == 'Installed 1 package(s)' + - install_4.packages == [extra_pkg_outfmt] + - install_5 is changed + - install_5.msg == 'Installed 3 package(s)' + - install_5.packages|sort() == [reg_pkg, url_pkg, file_pkg]|sort() 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 new file mode 100644 index 000000000..2271ebc03 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml @@ -0,0 +1,74 @@ +--- +# 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 + +- vars: + package_name: xinetd + config_file: /etc/xinetd.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' + state: absent + + - name: Install {{ package_name }} + pacman: + name: '{{ package_name }}' + state: present + + - name: Modify {{config_file}} + blockinfile: + path: '{{config_file}}' + block: | + # something something + # on 2 lines + + - name: Remove {{ package_name }} - generate pacsave + pacman: + name: '{{ package_name }}' + state: absent + - name: Make sure {{config_file}}.pacsave exists + stat: + path: '{{config_file}}.pacsave' + register: pacsave_st_1 + + - assert: + that: + - pacsave_st_1.stat.exists + + - name: Delete {{config_file}}.pacsave + file: + path: '{{config_file}}.pacsave' + state: absent + + - name: Install {{ package_name }} + pacman: + name: '{{ package_name }}' + state: present + + - name: Modify {{config_file}} + blockinfile: + path: '{{config_file}}' + block: | + # something something + # on 2 lines + + - name: Remove {{ package_name }} - nosave + pacman: + name: '{{ package_name }}' + remove_nosave: true + state: absent + + - name: Make sure {{config_file}}.pacsave does not exist + stat: + path: '{{config_file}}.pacsave' + register: pacsave_st_2 + + - assert: + that: + - not pacsave_st_2.stat.exists diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/update_cache.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/update_cache.yml new file mode 100644 index 000000000..ee2ac3b9f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/update_cache.yml @@ -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 + +- name: Make sure package cache is updated + pacman: + update_cache: true + +- name: Update package cache again (should not be changed) + pacman: + update_cache: true + register: update_cache_idem + +- name: Update package cache again with force=true (should be changed) + pacman: + update_cache: true + force: true + register: update_cache_force + +- name: Check conditions + assert: + that: + - update_cache_idem is not changed + - update_cache_idem.cache_updated == false + - update_cache_force is changed + - update_cache_force.cache_updated == true diff --git a/ansible_collections/community/general/tests/integration/targets/pagerduty_user/aliases b/ansible_collections/community/general/tests/integration/targets/pagerduty_user/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pagerduty_user/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/pagerduty_user/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pagerduty_user/tasks/main.yml new file mode 100644 index 000000000..13a0a5e09 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pagerduty_user/tasks/main.yml @@ -0,0 +1,25 @@ +# Test code for pagerduty_user module +# +# Copyright (c) 2020, Zainab Alsaffar +# 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 library + pip: + name: pdpyras + state: present + +- name: Create a user account on PagerDuty + pagerduty_user: + access_token: '{{ pd_api_access_token }}' + pd_user: '{{ fullname }}' + pd_email: '{{ email }}' + pd_role: '{{ pd_role }}' + pd_teams: '{{ pd_team }}' + state: present + +- name: Remove a user account from PagerDuty + pagerduty_user: + access_token: "{{ pd_api_access_token }}" + pd_user: "{{ fullname }}" + pd_email: "{{ email }}" + state: "absent" diff --git a/ansible_collections/community/general/tests/integration/targets/pagerduty_user/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/pagerduty_user/vars/main.yml new file mode 100644 index 000000000..723755727 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pagerduty_user/vars/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 + +pd_api_access_token: your_api_access_token +fullname: User Name +email: user@email.com +pd_role: observer +pd_teams: team1 diff --git a/ansible_collections/community/general/tests/integration/targets/pam_limits/aliases b/ansible_collections/community/general/tests/integration/targets/pam_limits/aliases new file mode 100644 index 000000000..b85ae6419 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pam_limits/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/freebsd +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/pam_limits/files/test_pam_limits.conf b/ansible_collections/community/general/tests/integration/targets/pam_limits/files/test_pam_limits.conf new file mode 100644 index 000000000..7d5d8bc85 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pam_limits/files/test_pam_limits.conf @@ -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 + +# /etc/security/limits.conf diff --git a/ansible_collections/community/general/tests/integration/targets/pam_limits/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pam_limits/tasks/main.yml new file mode 100644 index 000000000..5ad68f4a6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pam_limits/tasks/main.yml @@ -0,0 +1,92 @@ +# Test code for the pam_limits module +# Copyright (c) 2021, Abhijeet Kasurde +# 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: Set value for temp limit configuration + set_fact: + test_limit_file: "/tmp/limits.conf" + +- name: Copy temporary limits.conf + copy: + src: test_pam_limits.conf + dest: "{{ test_limit_file }}" + +- name: Test check mode support in pam_limits + community.general.pam_limits: + domain: smith + limit_type: soft + limit_item: nofile + value: '64000' + dest: "{{ test_limit_file }}" + check_mode: true + register: check_mode_test + +- name: Test that check mode is working + assert: + that: + - check_mode_test is changed + +- name: Add soft limit for smith user + community.general.pam_limits: + domain: smith + limit_type: soft + limit_item: nofile + value: '64000' + dest: "{{ test_limit_file }}" + register: soft_limit_test + +- name: Check if changes are made + assert: + that: + - soft_limit_test is changed + +- name: Aagin change soft limit for smith user for idempotency + community.general.pam_limits: + domain: smith + limit_type: soft + limit_item: nofile + value: '64000' + dest: "{{ test_limit_file }}" + register: soft_limit_test + +- name: Check if changes are not made idempotency + assert: + that: + - not soft_limit_test.changed + +- name: Change hard limit for Joe user for diff + community.general.pam_limits: + domain: joe + limit_type: hard + limit_item: nofile + value: '100000' + dest: "{{ test_limit_file }}" + register: hard_limit_test + diff: true + +- name: Debugging output for hard limit test + debug: + msg: "{{ hard_limit_test }}" + +- name: Check if changes made + assert: + that: + - hard_limit_test is changed + - hard_limit_test.diff.after is defined + - hard_limit_test.diff.before is defined + +- name: Add comment with change + community.general.pam_limits: + domain: doom + limit_type: hard + limit_item: nofile + value: '100000' + dest: "{{ test_limit_file }}" + comment: "This is a nice comment" + register: comment_test + +- name: Check if changes made + assert: + that: + - comment_test is changed diff --git a/ansible_collections/community/general/tests/integration/targets/pamd/aliases b/ansible_collections/community/general/tests/integration/targets/pamd/aliases new file mode 100644 index 000000000..b85ae6419 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pamd/aliases @@ -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 + +azp/posix/1 +skip/aix +skip/freebsd +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/pamd/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pamd/tasks/main.yml new file mode 100644 index 000000000..fdd16d166 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pamd/tasks/main.yml @@ -0,0 +1,74 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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: Set value for temp limit configuration + set_fact: + test_pamd_file: "/tmp/pamd_file" + +- name: Create temporary pam.d file + copy: + content: "session required pam_lastlog.so silent showfailed" + dest: "{{ test_pamd_file }}" +- name: Test working on a single-line file works (2925) + community.general.pamd: + path: /tmp + name: pamd_file + type: session + control: required + module_path: pam_lastlog.so + module_arguments: silent + state: args_absent + register: pamd_file_output +- name: Check if changes made + assert: + that: + - pamd_file_output is changed + +- name: Test removing all arguments from an entry (3260) + community.general.pamd: + path: /tmp + name: pamd_file + type: session + control: required + module_path: pam_lastlog.so + module_arguments: "" + state: updated + register: pamd_file_output_noargs +- name: Read back the file (3260) + slurp: + src: "{{ test_pamd_file }}" + register: pamd_file_slurp_noargs +- name: Check if changes made (3260) + vars: + line_array: "{{ (pamd_file_slurp_noargs.content|b64decode).split('\n')[2].split() }}" + assert: + that: + - pamd_file_output_noargs is changed + - line_array == ['session', 'required', 'pam_lastlog.so'] + +- name: Create temporary pam.d file + copy: + content: "" + dest: "{{ test_pamd_file }}" +# This test merely demonstrates that, as-is, module will not perform any changes on an empty file +# All the existing values for "state" will first search for a rule matching type, control, module_path +# and will not perform any change whatsoever if no existing rules match. +- name: Test working on a empty file works (2925) + community.general.pamd: + path: /tmp + name: pamd_file + type: session + control: required + module_path: pam_lastlog.so + module_arguments: silent + register: pamd_file_output_empty +- name: Read back the file + slurp: + src: "{{ test_pamd_file }}" + register: pamd_file_slurp +- name: Check if changes made + assert: + that: + - pamd_file_output_empty is not changed + - pamd_file_slurp.content|b64decode == '' diff --git a/ansible_collections/community/general/tests/integration/targets/parted/aliases b/ansible_collections/community/general/tests/integration/targets/parted/aliases new file mode 100644 index 000000000..b2b1b25fd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/parted/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 +azp/posix/vm +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/docker +needs/root +destructive diff --git a/ansible_collections/community/general/tests/integration/targets/parted/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/parted/handlers/main.yml new file mode 100644 index 000000000..e292260d0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/parted/handlers/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 + +- name: Remove loopback device + command: + cmd: losetup -d {{ losetup_name.stdout }} + changed_when: true + +- name: Remove file + file: + path: /bigfile + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/parted/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/parted/tasks/main.yml new file mode 100644 index 000000000..c91258d35 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/parted/tasks/main.yml @@ -0,0 +1,86 @@ +# Copyright (c) 2021, Alexei Znamensky +# 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 parted + package: + name: parted + state: present + when: ansible_os_family == 'Alpine' + +- name: Create empty file + community.general.filesize: + path: /bigfile + size: 1GiB + notify: Remove file + +- name: Obtain loop device name + command: + cmd: losetup -f + changed_when: false + register: losetup_name + +- name: Create loopback device + command: + cmd: losetup -f /bigfile + changed_when: true + register: losetup_cmd + notify: Remove loopback device + +- name: Create first partition + community.general.parted: + device: "{{ losetup_name.stdout }}" + number: 1 + state: present + fs_type: ext4 + part_end: "50%" + register: partition1 + +- name: Make filesystem + community.general.filesystem: + device: "{{ losetup_name.stdout }}p1" + fstype: ext4 + register: fs1_succ + +- name: Make filesystem (fail) + community.general.filesystem: + device: "{{ losetup_name.stdout }}p2" + fstype: ext4 + ignore_errors: true + register: fs_fail + +- name: Create second partition + community.general.parted: + device: "{{ losetup_name.stdout }}" + number: 2 + state: present + fs_type: ext4 + part_start: "{{ partition1.partitions[0].end + 1 }}KiB" + part_end: "100%" + register: partition2 + +- name: Make filesystem + community.general.filesystem: + device: "{{ losetup_name.stdout }}p2" + fstype: ext4 + register: fs2_succ + +- name: Remove first partition + community.general.parted: + device: "{{ losetup_name.stdout }}" + number: 1 + state: absent + register: partition_rem1 + +- name: Assert results + assert: + that: + - partition1 is changed + - fs1_succ is changed + - fs_fail is failed + - fs_fail is not changed + - partition2 is changed + - partition2.partitions | length == 2 + - fs2_succ is changed + - partition_rem1 is changed + - partition_rem1.partitions | length == 1 diff --git a/ansible_collections/community/general/tests/integration/targets/pids/aliases b/ansible_collections/community/general/tests/integration/targets/pids/aliases new file mode 100644 index 000000000..343f119da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pids/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/3 diff --git a/ansible_collections/community/general/tests/integration/targets/pids/files/sleeper.c b/ansible_collections/community/general/tests/integration/targets/pids/files/sleeper.c new file mode 100644 index 000000000..16d4b0eaa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pids/files/sleeper.c @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2022, Alexei Znamensky + * 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 +#include + +int main(int argc, char **argv) { + int delay = atoi(argv[1]); + sleep(delay); +} diff --git a/ansible_collections/community/general/tests/integration/targets/pids/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/pids/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pids/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/pids/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml new file mode 100644 index 000000000..2ba7f3754 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml @@ -0,0 +1,120 @@ +#################################################################### +# 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 pids module +# Copyright (c) 2019, Saranya Sridharan +# 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: Attempt installation of latest 'psutil' version + pip: + name: psutil + ignore_errors: true + register: psutil_latest_install + +- name: Install greatest 'psutil' version which will work with all pip versions + pip: + name: psutil < 5.7.0 + when: psutil_latest_install is failed + +- name: "Checking the empty result" + pids: + name: "blahblah" + register: emptypids + +- name: "Verify that the list of Process IDs (PIDs) returned is empty" + assert: + that: + - emptypids is not changed + - emptypids.pids == [] + +- name: "Picking a random process name" + set_fact: + random_name: some-random-long-name-{{ 10000000000 + (9999999999 | random) }} + +- name: Copy the fake 'sleep' source code + copy: + src: sleeper.c + dest: "{{ remote_tmp_dir }}/sleeper.c" + mode: 0644 + +- name: Compile fake 'sleep' binary + command: cc {{ remote_tmp_dir }}/sleeper.c -o {{ remote_tmp_dir }}/{{ random_name }} + +- name: Copy templated helper script + template: + src: obtainpid.sh + dest: "{{ remote_tmp_dir }}/obtainpid.sh" + mode: 0755 + +- name: "Run the fake 'sleep' binary" + command: sh {{ remote_tmp_dir }}/obtainpid.sh + async: 100 + poll: 0 + +- name: "Wait for one second to make sure that the fake 'sleep' binary has actually been started" + pause: + seconds: 1 + +- name: "Checking the process IDs (PIDs) of fake 'sleep' binary" + pids: + name: "{{ random_name }}" + register: pids + +- name: "Checking that exact non-substring matches are required" + pids: + name: "{{ random_name[0:25] }}" + register: exactpidmatch + +- name: "Checking that patterns can be used with the pattern option" + pids: + pattern: "{{ random_name[0:25] }}" + register: pattern_pid_match + +- name: "Checking that case-insensitive patterns can be used with the pattern option" + pids: + pattern: "{{ random_name[0:25] | upper }}" + ignore_case: true + register: caseinsensitive_pattern_pid_match + +- name: "Checking that .* includes test pid" + pids: + pattern: .* + register: match_all + +- name: "Reading pid from the file" + slurp: + src: "{{ remote_tmp_dir }}/obtainpid.txt" + register: newpid + +- name: Gather all processes to make debugging easier + command: ps aux + register: result + no_log: true + +- name: List all processes to make debugging easier + debug: + var: result.stdout_lines + +- name: "Verify that the Process IDs (PIDs) returned is not empty and also equal to the PIDs obtained in console" + assert: + that: + - "pids.pids | join(' ') == newpid.content | b64decode | trim" + - "pids.pids | length > 0" + - "exactpidmatch.pids == []" + - "pattern_pid_match.pids | join(' ') == newpid.content | b64decode | trim" + - "caseinsensitive_pattern_pid_match.pids | join(' ') == newpid.content | b64decode | trim" + - newpid.content | b64decode | trim | int in match_all.pids + +- name: "Register output of bad input pattern" + pids: + pattern: (unterminated + register: bad_pattern_result + ignore_errors: true + +- name: "Verify that bad input pattern result is failed" + assert: + that: + - bad_pattern_result is failed 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 new file mode 100644 index 000000000..ecbf56aab --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh @@ -0,0 +1,7 @@ +#!/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 + +"{{ remote_tmp_dir }}/{{ random_name }}" 100 & +echo "$!" > "{{ remote_tmp_dir }}/obtainpid.txt" diff --git a/ansible_collections/community/general/tests/integration/targets/pipx/aliases b/ansible_collections/community/general/tests/integration/targets/pipx/aliases new file mode 100644 index 000000000..9f87ec348 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pipx/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/python2 +skip/python3.5 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 new file mode 100644 index 000000000..567405ec4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml @@ -0,0 +1,316 @@ +--- +# 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 pipx + pip: + name: pipx + extra_args: --user + +############################################################################## +- name: ensure application tox is uninstalled + community.general.pipx: + state: absent + name: tox + register: uninstall_tox + +- name: install application tox + community.general.pipx: + name: tox + register: install_tox + +- name: set fact latest_tox_version + set_fact: + latest_tox_version: "{{ install_tox.application.tox.version }}" + +- name: install application tox again + community.general.pipx: + name: tox + register: install_tox_again + +- name: install application tox again force + community.general.pipx: + name: tox + force: true + register: install_tox_again_force + +- name: uninstall application tox + community.general.pipx: + state: absent + name: tox + register: uninstall_tox + +- name: check assertions tox + assert: + that: + - install_tox is changed + - "'tox' in install_tox.application" + - install_tox_again is not changed + - install_tox_again_force is changed + - uninstall_tox is changed + - "'tox' not in uninstall_tox.application" + +############################################################################## +- name: install application tox with system-site-packages + community.general.pipx: + name: tox + system_site_packages: true + register: install_tox + +- name: get raw pipx_info + community.general.pipx_info: + include_raw: true + register: pipx_info_raw + +- name: uninstall application tox + community.general.pipx: + state: absent + name: tox + register: uninstall_tox + +- name: check assertions tox + assert: + that: + - install_tox is changed + - "'tox' in install_tox.application" + - pipx_info_raw is not changed + - "'--system-site-packages' in pipx_info_raw.raw_output.venvs.tox.metadata.venv_args" + - uninstall_tox is changed + - "'tox' not in uninstall_tox.application" + +############################################################################## +- name: install application tox 3.24.0 + community.general.pipx: + name: tox + source: tox==3.24.0 + register: install_tox_324 + +- name: reinstall tox 3.24.0 + community.general.pipx: + name: tox + state: reinstall + register: reinstall_tox_324 + +- name: reinstall without name + community.general.pipx: + state: reinstall + register: reinstall_noname + ignore_errors: true + +- name: upgrade tox from 3.24.0 + community.general.pipx: + name: tox + state: upgrade + register: upgrade_tox_324 + +- name: upgrade without name + community.general.pipx: + state: upgrade + register: upgrade_noname + ignore_errors: true + +- name: downgrade tox 3.24.0 + community.general.pipx: + name: tox + source: tox==3.24.0 + force: true + register: downgrade_tox_324 + +- name: cleanup tox 3.24.0 + community.general.pipx: + state: absent + name: tox + register: uninstall_tox_324 + +- name: check assertions tox 3.24.0 + assert: + that: + - install_tox_324 is changed + - "'tox' in install_tox_324.application" + - install_tox_324.application.tox.version == '3.24.0' + - reinstall_tox_324 is changed + - reinstall_tox_324.application.tox.version == '3.24.0' + - upgrade_tox_324 is changed + - upgrade_tox_324.application.tox.version != '3.24.0' + - downgrade_tox_324 is changed + - downgrade_tox_324.application.tox.version == '3.24.0' + - uninstall_tox_324 is changed + - "'tox' not in uninstall_tox_324.application" + - upgrade_noname is failed + - reinstall_noname is failed + +############################################################################## +- name: install application latest tox + community.general.pipx: + name: tox + state: latest + register: install_tox_latest + +- name: cleanup tox latest + community.general.pipx: + state: absent + name: tox + register: uninstall_tox_latest + +- name: install application tox 3.24.0 for latest + community.general.pipx: + name: tox + source: tox==3.24.0 + register: install_tox_324_for_latest + +- name: install application latest tox + community.general.pipx: + name: tox + state: latest + register: install_tox_latest_with_preinstall + +- name: install application latest tox again + community.general.pipx: + name: tox + state: latest + register: install_tox_latest_with_preinstall_again + +- name: install application latest tox + community.general.pipx: + name: tox + state: latest + force: true + register: install_tox_latest_with_preinstall_again_force + +- name: cleanup tox latest again + community.general.pipx: + state: absent + name: tox + register: uninstall_tox_latest_again + +- name: install application tox with deps + community.general.pipx: + state: latest + name: tox + install_deps: true + register: install_tox_with_deps + +- name: cleanup tox latest yet again + community.general.pipx: + state: absent + name: tox + register: uninstall_tox_again + +- name: check assertions tox latest + assert: + that: + - install_tox_latest is changed + - uninstall_tox_latest is changed + - install_tox_324_for_latest is changed + - install_tox_324_for_latest.application.tox.version == '3.24.0' + - install_tox_latest_with_preinstall is changed + - install_tox_latest_with_preinstall.application.tox.version == latest_tox_version + - install_tox_latest_with_preinstall_again is not changed + - install_tox_latest_with_preinstall_again.application.tox.version == latest_tox_version + - install_tox_latest_with_preinstall_again_force is changed + - install_tox_latest_with_preinstall_again_force.application.tox.version == latest_tox_version + - uninstall_tox_latest_again is changed + - install_tox_with_deps is changed + - install_tox_with_deps.application.tox.version == latest_tox_version + - uninstall_tox_again is changed + - "'tox' not in uninstall_tox_again.application" + +############################################################################## +- name: ensure application ansible-lint is uninstalled + community.general.pipx: + name: ansible-lint + state: absent + +- name: install application ansible-lint + community.general.pipx: + name: ansible-lint + register: install_ansible_lint + +- name: inject packages + community.general.pipx: + state: inject + name: ansible-lint + inject_packages: + - licenses + register: inject_pkgs_ansible_lint + +- name: inject packages with apps + community.general.pipx: + state: inject + name: ansible-lint + inject_packages: + - black + install_apps: true + register: inject_pkgs_apps_ansible_lint + +- name: cleanup ansible-lint + community.general.pipx: + state: absent + name: ansible-lint + register: uninstall_ansible_lint + +- name: check assertions inject_packages + assert: + that: + - install_ansible_lint is changed + - inject_pkgs_ansible_lint is changed + - '"ansible-lint" in inject_pkgs_ansible_lint.application' + - '"licenses" in inject_pkgs_ansible_lint.application["ansible-lint"]["injected"]' + - inject_pkgs_apps_ansible_lint is changed + - '"ansible-lint" in inject_pkgs_apps_ansible_lint.application' + - '"black" in inject_pkgs_apps_ansible_lint.application["ansible-lint"]["injected"]' + - uninstall_ansible_lint is changed + +############################################################################## +- name: install jupyter - not working smoothly in freebsd + when: ansible_system != 'FreeBSD' + block: + - name: ensure application jupyter is uninstalled + community.general.pipx: + name: jupyter + state: absent + + - name: install application jupyter + community.general.pipx: + name: jupyter + install_deps: true + register: install_jupyter + + - name: cleanup jupyter + community.general.pipx: + state: absent + name: jupyter + + - name: check assertions + assert: + that: + - install_jupyter is changed + - '"ipython" in install_jupyter.stdout' + +############################################################################## +- name: ensure /opt/pipx + ansible.builtin.file: + path: /opt/pipx + state: directory + mode: 0755 + +- name: install tox site-wide + community.general.pipx: + name: tox + state: latest + register: install_tox_sitewide + environment: + PIPX_HOME: /opt/pipx + PIPX_BIN_DIR: /usr/local/bin + +- name: stat /usr/local/bin/tox + ansible.builtin.stat: + path: /usr/local/bin/tox + register: usrlocaltox + +- name: check assertions + ansible.builtin.assert: + that: + - install_tox_sitewide is changed + - usrlocaltox.stat.exists diff --git a/ansible_collections/community/general/tests/integration/targets/pipx_info/aliases b/ansible_collections/community/general/tests/integration/targets/pipx_info/aliases new file mode 100644 index 000000000..a28278bbc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pipx_info/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/3 +destructive +skip/python2 +skip/python3.5 diff --git a/ansible_collections/community/general/tests/integration/targets/pipx_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pipx_info/tasks/main.yml new file mode 100644 index 000000000..0a01f0af9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pipx_info/tasks/main.yml @@ -0,0 +1,140 @@ +--- +# 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 pipx + pip: + name: pipx + extra_args: --user + +############################################################################## +- name: ensure application tox is uninstalled + community.general.pipx: + state: absent + name: tox + +- name: retrieve applications (empty) + community.general.pipx_info: {} + register: info_empty + +- name: install application tox + community.general.pipx: + name: tox + +- name: retrieve applications + community.general.pipx_info: {} + register: info_all + +- name: retrieve applications (include_deps=true) + community.general.pipx_info: + include_deps: true + register: info_all_deps + +- name: retrieve application tox + community.general.pipx_info: + name: tox + include_deps: true + register: info_tox + +- name: uninstall application tox + community.general.pipx: + state: absent + name: tox + +- name: check assertions tox + assert: + that: + - info_empty.application|length == 0 + + - info_all.application|length == 1 + - info_all.application[0].name == "tox" + - "'version' in info_all.application[0]" + - "'dependencies' not in info_all.application[0]" + - "'injected' not in info_all.application[0]" + + - info_all_deps.application|length == 1 + - info_all_deps.application[0].name == "tox" + - "'version' in info_all_deps.application[0]" + - info_all_deps.application[0].dependencies == ["chardet", "virtualenv"] + or info_all_deps.application[0].dependencies == ["virtualenv"] + - "'injected' not in info_all.application[0]" + + - info_tox.application == info_all_deps.application + +############################################################################## +- name: set test applications + set_fact: + apps: + - name: tox + source: tox==3.24.0 + - name: ansible-lint + inject_packages: + - licenses + +- name: ensure applications are uninstalled + community.general.pipx: + name: "{{ item.name }}" + state: absent + loop: "{{ apps }}" + +- name: install applications + community.general.pipx: + name: "{{ item.name }}" + source: "{{ item.source|default(omit) }}" + loop: "{{ apps }}" + +- name: inject packages + community.general.pipx: + state: inject + name: "{{ item.name }}" + inject_packages: "{{ item.inject_packages }}" + when: "'inject_packages' in item" + loop: "{{ apps }}" + +- name: retrieve applications + community.general.pipx_info: {} + register: info2_all + +- name: retrieve applications (include_deps=true) + community.general.pipx_info: + include_deps: true + include_injected: true + register: info2_all_deps + +- name: retrieve application ansible-lint + community.general.pipx_info: + name: ansible-lint + include_deps: true + include_injected: true + register: info2_lint + +- name: ensure applications are uninstalled + community.general.pipx: + name: "{{ item.name }}" + state: absent + loop: "{{ apps }}" + +- name: check assertions multiple apps + assert: + that: + - all_apps|length == 2 + - all_apps[1].name == "tox" + - all_apps[1].version == "3.24.0" + - "'dependencies' not in all_apps[1]" + - "'injected' not in all_apps[1]" + + - all_apps_deps|length == 2 + - all_apps_deps[1].name == "tox" + - all_apps_deps[1].version == "3.24.0" + - all_apps_deps[1].dependencies == ["virtualenv"] + - "'injected' in all_apps_deps[0]" + - "'licenses' in all_apps_deps[0].injected" + + - lint|length == 1 + - all_apps_deps|length == 2 + - lint[0] == all_apps_deps[0] + vars: + all_apps: "{{ info2_all.application|sort(attribute='name') }}" + all_apps_deps: "{{ info2_all_deps.application|sort(attribute='name') }}" + lint: "{{ info2_lint.application|sort(attribute='name') }}" diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/aliases b/ansible_collections/community/general/tests/integration/targets/pkgng/aliases new file mode 100644 index 000000000..e13fde32c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/aliases @@ -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 + +azp/posix/1 +needs/root +skip/docker +skip/osx +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/create-outofdate-pkg.yml b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/create-outofdate-pkg.yml new file mode 100644 index 000000000..4028c57d8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/create-outofdate-pkg.yml @@ -0,0 +1,52 @@ +--- +# 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 temporary directory for package creation + tempfile: + state: directory + register: pkgng_test_outofdate_pkg_tempdir + +- name: Copy intentionally out-of-date package manifest to testhost + template: + src: MANIFEST.json.j2 + # Plus-sign must be added at the destination + # CI doesn't like files with '+' in them in the repository + dest: '{{ pkgng_test_outofdate_pkg_tempdir.path }}/MANIFEST' + +- name: Create out-of-date test package file + command: + argv: + - pkg + - create + - '--verbose' + - '--out-dir' + - '{{ pkgng_test_outofdate_pkg_tempdir.path }}' + - '--manifest' + - '{{ pkgng_test_outofdate_pkg_tempdir.path }}/MANIFEST' + +# pkg switched from .txz to .pkg in version 1.17.0 +# Might as well look for all valid pkg extensions. +- name: Find created package file + find: + path: '{{ pkgng_test_outofdate_pkg_tempdir.path }}' + use_regex: true + pattern: '.*\.(pkg|tzst|t[xbg]z|tar)' + register: pkgng_test_outofdate_pkg_tempfile + +- name: There should be only one package + assert: + that: + - pkgng_test_outofdate_pkg_tempfile.files | count == 1 + +- name: Copy the created package file to the expected location + copy: + remote_src: true + src: '{{ pkgng_test_outofdate_pkg_tempfile.files[0].path }}' + dest: '{{ pkgng_test_outofdate_pkg_path }}' + +- name: Remove temporary directory + file: + state: absent + path: '{{ pkgng_test_outofdate_pkg_tempdir.path }}' 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 new file mode 100644 index 000000000..0c8001899 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml @@ -0,0 +1,551 @@ +--- +# 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 + +## +## pkgng - prepare test environment +## +- name: Remove test package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + +## +## pkgng - example - state=present for single package +## +- name: 'state=present for single package' + include_tasks: install_single_package.yml + +## +## pkgng - example - state=latest for already up-to-date package +## +- name: Upgrade package (idempotent) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: latest + register: pkgng_example2 + +- name: Ensure pkgng does not upgrade up-to-date package + assert: + that: + - not pkgng_example2.changed + +## +## pkgng - example - state=absent for single package +## +- name: Verify package sentinel file is present + stat: + path: '{{ pkgng_test_pkg_sentinelfile_path }}' + get_attributes: false + get_checksum: false + get_mime: false + register: pkgng_example3_stat_before + +- name: Install package (checkmode) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + check_mode: true + register: pkgng_example3_checkmode + +- name: Remove package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + register: pkgng_example3 + +- name: Remove package (idempotent) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + register: pkgng_example3_idempotent + +- name: Verify package sentinel file is not present + stat: + path: '{{ pkgng_test_pkg_sentinelfile_path }}' + get_attributes: false + get_checksum: false + get_mime: false + register: pkgng_example3_stat_after + +- name: Ensure pkgng installs package correctly + assert: + that: + - pkgng_example3_stat_before.stat.exists + - pkgng_example3_stat_before.stat.executable + - not pkgng_example3_checkmode.changed + - pkgng_example3.changed + - not pkgng_example3_idempotent.changed + - not pkgng_example3_stat_after.stat.exists + +## +## pkgng - example - state=latest for out-of-date package +## +- name: Install intentionally out-of-date package and upgrade it + # + # NOTE: The out-of-date package provided is a minimal, + # no-contents test package that declares {{ pkgng_test_pkg_name }} with + # a version of 0, so it should always be upgraded. + # + # This test might fail at some point in the + # future if the FreeBSD package format receives + # breaking changes that prevent pkg from installing + # older package formats. + # + block: + - name: Create out-of-date test package + import_tasks: create-outofdate-pkg.yml + + - name: Install out-of-date test package + command: 'pkg add {{ pkgng_test_outofdate_pkg_path }}' + register: pkgng_example4_prepare + + - name: Check for any available package upgrades (checkmode) + pkgng: + name: '*' + state: latest + check_mode: true + register: pkgng_example4_wildcard_checkmode + + - name: Check for available package upgrade (checkmode) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: latest + check_mode: true + register: pkgng_example4_checkmode + + - name: Upgrade out-of-date package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: latest + register: pkgng_example4 + + - name: Upgrade out-of-date package (idempotent) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: latest + register: pkgng_example4_idempotent + + - name: Remove test out-of-date package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + + - name: Ensure pkgng upgrades package correctly + assert: + that: + - not pkgng_example4_prepare.failed + - pkgng_example4_wildcard_checkmode.changed + - pkgng_example4_checkmode.changed + - pkgng_example4.changed + - not pkgng_example4_idempotent.changed + +## +## pkgng - example - state=latest for out-of-date package without privileges +## +- name: Install intentionally out-of-date package and try to upgrade it with unprivileged user + block: + - ansible.builtin.user: + name: powerless + shell: /bin/bash + + - name: Create out-of-date test package + import_tasks: create-outofdate-pkg.yml + + - name: Install out-of-date test package + command: 'pkg add {{ pkgng_test_outofdate_pkg_path }}' + register: pkgng_example4_nopower_prepare + + - name: Check for any available package upgrades with unprivileged user + become: true + become_user: powerless + pkgng: + name: '*' + state: latest + register: pkgng_example4_nopower_wildcard + ignore_errors: true + + - name: Remove test out-of-date package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + + - name: Ensure pkgng upgrades package correctly + assert: + that: + - not pkgng_example4_nopower_prepare.failed + - pkgng_example4_nopower_wildcard.failed + +## +## pkgng - example - Install multiple packages in one command +## +- name: Remove test package (checkmode) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + check_mode: true + register: pkgng_example5_prepare + +- name: Install three packages + pkgng: + name: + - '{{ pkgng_test_pkg_name }}' + - fish + - busybox + register: pkgng_example5 + +- name: Remove three packages + pkgng: + name: + - '{{ pkgng_test_pkg_name }}' + - fish + - busybox + state: absent + register: pkgng_example5_cleanup + +- name: Ensure pkgng installs multiple packages with one command + assert: + that: + - not pkgng_example5_prepare.changed + - pkgng_example5.changed + - '(pkgng_example5.stdout | regex_search("^Number of packages to be installed: (\d+)", "\\1", multiline=True) | first | int) >= 3' + - '(pkgng_example5.stdout | regex_findall("^Number of packages to be", multiline=True) | count) == 1' + - pkgng_example5_cleanup.changed + +## +## pkgng - example - state=latest multiple packages, some already installed +## +- name: Remove test package (checkmode) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + state: absent + check_mode: true + register: pkgng_example6_check + +- name: Create out-of-date test package + import_tasks: create-outofdate-pkg.yml + +- name: Install out-of-date test package + command: 'pkg add {{ pkgng_test_outofdate_pkg_path }}' + register: pkgng_example6_prepare + +- name: Upgrade and/or install two packages + pkgng: + name: + - '{{ pkgng_test_pkg_name }}' + - fish + state: latest + register: pkgng_example6 + +- name: Remove two packages + pkgng: + name: + - '{{ pkgng_test_pkg_name }}' + - fish + state: absent + register: pkgng_example6_cleanup + +- name: Ensure pkgng installs multiple packages with one command + assert: + that: + - not pkgng_example6_check.changed + - not pkgng_example6_prepare.failed + - pkgng_example6.changed + - '(pkgng_example6.stdout | regex_search("^Number of packages to be installed: (\d+)", "\\1", multiline=True) | first | int) >= 1' + - '(pkgng_example6.stdout | regex_search("^Number of packages to be upgraded: (\d+)", "\\1", multiline=True) | first | int) >= 1' + # Checking that "will be affected" occurs twice in the output ensures + # that the module runs two separate commands for install and upgrade, + # as the pkg command only outputs the string once per invocation. + - '(pkgng_example6.stdout | regex_findall("will be affected", multiline=True) | count) == 2' + - pkgng_example6_cleanup.changed + +## +## pkgng - example - autoremove=yes +## +- name: "Test autoremove=yes" + # + # NOTE: FreeBSD 12.0 test runner receives a "connection reset by peer" after ~20% downloaded so we are + # only running this on 12.1 or higher + # + when: ansible_distribution_version is version('12.01', '>=') + block: + - name: Install GNU autotools + pkgng: + name: autotools + state: latest + register: pkgng_example7_prepare_install + + - name: Remove GNU autotools and run pkg autoremove + pkgng: + name: autotools + state: absent + autoremove: true + register: pkgng_example7 + + - name: Check if autoremove uninstalled known autotools dependencies + pkgng: + name: + - autoconf + - automake + - libtool + state: absent + check_mode: true + register: pkgng_example7_cleanup + + - name: Ensure pkgng autoremove works correctly + assert: + that: + - pkgng_example7_prepare_install is changed + - "'autoremoved' is in(pkgng_example7.msg)" + - pkgng_example7_cleanup is not changed + +## +## pkgng - example - single annotations +## +- name: Install and annotate single package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '+ansibletest_example8=added' + register: pkgng_example8_add_annotation + +- name: Should fail to add duplicate annotation + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '+ansibletest_example8=duplicate' + ignore_errors: true + register: pkgng_example8_add_annotation_failure + +- name: Verify annotation is actually there + command: 'pkg annotate -q -S {{ pkgng_test_pkg_name }} ansibletest_example8' + register: pkgng_example8_add_annotation_verify + +- name: Install and annotate single package (checkmode, not changed) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '+ansibletest_example8=added' + check_mode: true + register: pkgng_example8_add_annotation_checkmode_nochange + +- name: Install and annotate single package (checkmode, changed) + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '+ansibletest_example8_checkmode=added' + check_mode: true + register: pkgng_example8_add_annotation_checkmode_change + +- name: Verify check_mode did not add an annotation + command: 'pkg annotate -q -S {{ pkgng_test_pkg_name }} ansibletest_example8_checkmode' + register: pkgng_example8_add_annotation_checkmode_change_verify + +- name: Modify annotation on single package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: ':ansibletest_example8=modified' + register: pkgng_example8_modify_annotation + +- name: Should fail to modify missing annotation + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: ':ansiblemissing=modified' + ignore_errors: true + register: pkgng_example8_modify_annotation_failure + +- name: Verify annotation has been modified + command: 'pkg annotate -q -S {{ pkgng_test_pkg_name }} ansibletest_example8' + register: pkgng_example8_modify_annotation_verify + +- name: Remove annotation on single package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '-ansibletest_example8' + register: pkgng_example8_remove_annotation + +- name: Verify annotation has been removed + command: 'pkg annotate -q -S {{ pkgng_test_pkg_name }} ansibletest_example8' + register: pkgng_example8_remove_annotation_verify + +- name: Ensure pkgng annotations on single packages work correctly + assert: + that: + - pkgng_example8_add_annotation.changed + - pkgng_example8_add_annotation_failure.failed + - pkgng_example8_add_annotation_checkmode_nochange is not changed + - pkgng_example8_add_annotation_checkmode_change is changed + - 'pkgng_example8_add_annotation_checkmode_change_verify.stdout_lines | count == 0' + - 'pkgng_example8_add_annotation_verify.stdout_lines | first == "added"' + - pkgng_example8_modify_annotation.changed + - pkgng_example8_modify_annotation_failure.failed + - 'pkgng_example8_modify_annotation_verify.stdout_lines | first == "modified"' + - pkgng_example8_remove_annotation.changed + - 'pkgng_example8_remove_annotation_verify.stdout_lines | count == 0' + +## +## pkgng - example - multiple annotations +## +- name: Annotate single package with multiple annotations + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: + - '+ansibletest_example9_1=added' + - '+ansibletest_example9_2=added' + register: pkgng_example9_add_annotation + +- name: Verify annotation is actually there + command: 'pkg info -q -A {{ pkgng_test_pkg_name }}' + register: pkgng_example9_add_annotation_verify + # Assert, below, tests that stdout includes: + # ``` + # ansibletest_example9_1 : added + # ansibletest_example9_2 : added + # ``` + +- name: Multiple annotation operations on single package + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: + - ':ansibletest_example9_1=modified' + - '+ansibletest_example9_3=added' + register: pkgng_example9_multiple_annotation + +- name: Verify multiple operations succeeded + command: 'pkg info -q -A {{ pkgng_test_pkg_name }}' + register: pkgng_example9_multiple_annotation_verify + # Assert, below, tests that stdout includes: + # ``` + # ansibletest_example9_1 : modified + # ansibletest_example9_2 : added + # ansibletest_example9_3 : added + # ``` + +- name: Add multiple annotations with old syntax + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '+ansibletest_example9_4=added,+ansibletest_example9_5=added' + register: pkgng_example9_add_annotation_old + +- name: Verify annotation is actually there + command: 'pkg info -q -A {{ pkgng_test_pkg_name }}' + register: pkgng_example9_add_annotation_old_verify + # Assert, below, tests that stdout includes: + # ``` + # ansibletest_example9_4 : added + # ansibletest_example9_5 : added + # ``` + +- name: Ensure multiple annotations work correctly + assert: + that: + - pkgng_example9_add_annotation.changed + - '(pkgng_example9_add_annotation_verify.stdout_lines | select("match", "ansibletest_example9_[12]\s*:\s*added") | list | count) == 2' + - pkgng_example9_multiple_annotation.changed + - '(pkgng_example9_multiple_annotation_verify.stdout_lines | select("match", "ansibletest_example9_1\s*:\s*modified") | list | count) == 1' + - '(pkgng_example9_multiple_annotation_verify.stdout_lines | select("match", "ansibletest_example9_[23]\s*:\s*added") | list | count) == 2' + - pkgng_example9_add_annotation_old.changed + - '(pkgng_example9_add_annotation_old_verify.stdout_lines | select("match", "ansibletest_example9_[45]\s*:\s*added") | list | count) == 2' + +## +## pkgng - example - invalid annotation strings +## +- name: Should fail on invalid annotate strings + pkgng: + name: '{{ pkgng_test_pkg_name }}' + annotation: '{{ item }}' + ignore_errors: true + register: pkgng_example8_invalid_annotation_failure + loop: + - 'naked_string' + - '/invalid_operation' + - ',empty_first_tag=validsecond' + - '=notag' + +- name: Verify invalid annotate strings did not add annotations + command: 'pkg info -q -A {{ pkgng_test_pkg_name }}' + register: pkgng_example8_invalid_annotation_verify + +- name: Ensure invalid annotate strings fail safely + assert: + that: + # Invalid strings should not change anything + - '(pkgng_example8_invalid_annotation_failure.results | selectattr("changed") | list | count) == 0' + # Invalid strings should always fail + - '(pkgng_example8_invalid_annotation_failure.results | rejectattr("failed") | list | count) == 0' + # Invalid strings should not cause an exception + - '(pkgng_example8_invalid_annotation_failure.results | selectattr("exception", "defined") | list | count) == 0' + # Verify annotations are unaffected + - '(pkgng_example8_invalid_annotation_verify.stdout_lines | select("search", "(naked_string|invalid_operation|empty_first_tag|validsecond|notag)") | list | count) == 0' + +## +## pkgng - example - pkgsite=... +## +# NOTE: testing for failure here to not have to set up our own +# or depend on a third-party, alternate package repo +- name: Should fail with invalid pkgsite + pkgng: + name: '{{ pkgng_test_pkg_name }}' + pkgsite: DoesNotExist + ignore_errors: true + register: pkgng_example10_invalid_pkgsite_failure + +- name: Ensure invalid pkgsite fails as expected + assert: + that: + - pkgng_example10_invalid_pkgsite_failure.failed + - 'pkgng_example10_invalid_pkgsite_failure.stdout is search("^No repositories are enabled.", multiline=True)' + +## +## pkgng - example - Install single package in jail +## +- name: Test within jail + # + # NOTE: FreeBSD 12.0 test runner receives a "connection reset by peer" after ~20% downloaded so we are + # only running this on 12.1 or higher + # + # NOTE: FreeBSD 12.3 fails with some kernel mismatch for packages + # (someone with FreeBSD knowledge has to take a look) + # + # NOTE: FreeBSD 12.4 fails to update repositories because it cannot load certificates from /usr/share/keys/pkg/trusted + # (someone with FreeBSD knowledge has to take a look) + # + # NOTE: FreeBSD 13.0 fails to update the package catalogue for unknown reasons (someone with FreeBSD + # knowledge has to take a look) + # + # NOTE: FreeBSD 13.1 fails to update the package catalogue for unknown reasons (someone with FreeBSD + # knowledge has to take a look) + # + # NOTE: FreeBSD 13.2 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', '>=') + block: + - name: Setup testjail + include_tasks: setup-testjail.yml + + - name: Install package in jail as rootdir + include_tasks: install_single_package.yml + vars: + pkgng_test_rootdir: /usr/jails/testjail + pkgng_test_install_prefix: /usr/jails/testjail + pkgng_test_install_cleanup: true + + - name: Install package in jail + include_tasks: install_single_package.yml + vars: + pkgng_test_jail: testjail + pkgng_test_install_prefix: /usr/jails/testjail + pkgng_test_install_cleanup: true + + - name: Install package in jail as chroot + include_tasks: install_single_package.yml + vars: + pkgng_test_chroot: /usr/jails/testjail + pkgng_test_install_prefix: /usr/jails/testjail + pkgng_test_install_cleanup: true + always: + - name: Stop and remove testjail + failed_when: false + changed_when: false + command: "ezjail-admin delete -wf testjail" diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/install_single_package.yml b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/install_single_package.yml new file mode 100644 index 000000000..5ba529af3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/install_single_package.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: Verify package sentinel file is not present + stat: + path: '{{ pkgng_test_install_prefix | default("") }}{{ pkgng_test_pkg_sentinelfile_path }}' + get_attributes: false + get_checksum: false + get_mime: false + register: pkgng_install_stat_before + +- name: Install package + pkgng: &pkgng_install_params + name: '{{ pkgng_test_pkg_name }}' + jail: '{{ pkgng_test_jail | default(omit) }}' + chroot: '{{ pkgng_test_chroot | default(omit) }}' + rootdir: '{{ pkgng_test_rootdir | default(omit) }}' + register: pkgng_install + +- name: Remove package (checkmode) + pkgng: + <<: *pkgng_install_params + state: absent + check_mode: true + register: pkgng_install_checkmode + +- name: Install package (idempotent, cached) + pkgng: + <<: *pkgng_install_params + cached: true + register: pkgng_install_idempotent_cached + +- name: Verify package sentinel file is present + stat: + path: '{{ pkgng_test_install_prefix | default("") }}{{ pkgng_test_pkg_sentinelfile_path }}' + get_attributes: false + get_checksum: false + get_mime: false + register: pkgng_install_stat_after + +- name: Remove test package (if requested) + pkgng: + <<: *pkgng_install_params + state: absent + when: 'pkgng_test_install_cleanup | default(False)' + +- name: Ensure pkgng installs package correctly + assert: + that: + - not pkgng_install_stat_before.stat.exists + - pkgng_install.changed + - pkgng_install_checkmode.changed + - not pkgng_install_idempotent_cached.changed + - not pkgng_install_idempotent_cached.stdout is match("Updating \w+ repository catalogue\.\.\.") + - pkgng_install_stat_after.stat.exists + - pkgng_install_stat_after.stat.executable diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/main.yml new file mode 100644 index 000000000..59ca83d9f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/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 + +- import_tasks: freebsd.yml + when: + - ansible_facts.distribution == 'FreeBSD' diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/setup-testjail.yml b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/setup-testjail.yml new file mode 100644 index 000000000..3055d29e8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/setup-testjail.yml @@ -0,0 +1,100 @@ +--- +# 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 + +# +# Instructions for setting up a jail +# https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/jails-ezjail.html +# +- name: Setup cloned interfaces + lineinfile: + dest: /etc/rc.conf + regexp: ^cloned_interfaces=lo1 + line: cloned_interfaces=lo1 + +- name: Activate cloned interfaces + command: "service netif cloneup" + changed_when: false + +- name: Add nat rule for cloned interfaces + copy: + dest: /etc/pf.conf + content: | + nat on {{ ansible_default_ipv4.interface }} from 127.0.1.0/24 -> {{ ansible_default_ipv4.interface }}:0 + validate: "pfctl -nf %s" + +- name: Start pf firewall + service: + name: pf + state: started + enabled: true + +- name: Install ezjail + pkgng: + name: ezjail + +- name: Configure ezjail to use http + when: ansible_distribution_version is version('11.01', '>') + lineinfile: + dest: /usr/local/etc/ezjail.conf + regexp: ^ezjail_ftphost + line: ezjail_ftphost=http://ftp.freebsd.org + +- name: Configure ezjail to use archive for old freebsd releases + when: ansible_distribution_version is version('11.01', '<=') + lineinfile: + dest: /usr/local/etc/ezjail.conf + regexp: ^ezjail_ftphost + line: ezjail_ftphost=http://ftp-archive.freebsd.org + +- name: Start ezjail + ignore_errors: true + service: + name: ezjail + state: started + enabled: true + +- name: Redirect logs depending on verbosity + set_fact: + pkgng_jail_log_redirect: "2>&1 | tee -a /tmp/ezjail.log {{ '> /dev/null' if ansible_verbosity < 2 else '' }}" + +- name: Has ezjail + register: ezjail_base_jail + stat: + path: /usr/jails/basejail + +- name: Setup ezjail base + when: not ezjail_base_jail.stat.exists + shell: "ezjail-admin install {{ pkgng_jail_log_redirect }}" + changed_when: false + +- name: Has testjail + register: ezjail_test_jail + stat: + path: /usr/jails/testjail + +- name: Create testjail + when: not ezjail_test_jail.stat.exists + shell: "ezjail-admin create testjail 'lo1|127.0.1.1' {{ pkgng_jail_log_redirect }}" + changed_when: false + +- name: Configure testjail to use Cloudflare DNS + lineinfile: + dest: /usr/jails/testjail/etc/resolv.conf + regexp: "^nameserver[[:blank:]]+{{ item }}$" + line: "nameserver {{ item }}" + create: true + loop: + - "1.1.1.1" + - "1.0.0.1" + +- name: Is testjail running + shell: "jls | grep testjail" + changed_when: false + failed_when: false + register: is_testjail_up + +- name: Start testjail + when: is_testjail_up.rc == 1 + command: "ezjail-admin start testjail" diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2 b/ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2 new file mode 100644 index 000000000..e8537e89b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2 @@ -0,0 +1,16 @@ +{ + "name": "{{ pkgng_test_pkg_name }}", + "origin": "{{ pkgng_test_pkg_category }}/{{ pkgng_test_pkg_name }}", + "version": "{{ pkgng_test_pkg_version | default('0') }}", + "comment": "{{ pkgng_test_pkg_name }} (Ansible Integration Test Package)", + "maintainer": "ansible-devel@googlegroups.com", + "www": "https://github.com/ansible-collections/community.general", + "abi": "FreeBSD:*:*", + "arch": "freebsd:*:*", + "prefix": "/usr/local", + "flatsize":0, + "licenselogic": "single", + "licenses":["GPLv3"], + "desc": "This package is only installed temporarily for integration testing of the community.general.pkgng Ansible module.\nIts version number is 0 so that ANY version of the real package, with the same name, will be considered an upgrade.\nIts architecture and abi are FreeBSD:*:* so that it will install on any version or architecture of FreeBSD,\nthus future-proof as long as the package MANIFEST format does not change\nand a wildcard in the version portion of the abi or arch field is not prohibited.", + "categories":["{{ pkgng_test_pkg_category }}"] +} diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2.license b/ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/templates/MANIFEST.json.j2.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/pkgng/vars/main.yml new file mode 100644 index 000000000..e32cc4110 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgng/vars/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 + +pkgng_test_outofdate_pkg_path: "/tmp/ansible_pkgng_test_package.pkg" +pkgng_test_pkg_name: zsh +pkgng_test_pkg_category: shells +pkgng_test_pkg_sentinelfile_path: /usr/local/bin/zsh diff --git a/ansible_collections/community/general/tests/integration/targets/pkgutil/aliases b/ansible_collections/community/general/tests/integration/targets/pkgutil/aliases new file mode 100644 index 000000000..4232e0baa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgutil/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 + +destructive +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/pkgutil/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pkgutil/tasks/main.yml new file mode 100644 index 000000000..8ceb4adcc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/pkgutil/tasks/main.yml @@ -0,0 +1,117 @@ +# Test code for the pkgutil module + +# Copyright (c) 2019, Dag Wieers (@dagwieers) +# 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 + + +# CLEAN ENVIRONMENT +- name: Remove CSWtop + pkgutil: + name: CSWtop + state: absent + register: originally_installed + + +# ADD PACKAGE +- name: Add package (check_mode) + pkgutil: + name: CSWtop + state: present + check_mode: true + register: cm_add_package + +- name: Verify cm_add_package + assert: + that: + - cm_add_package is changed + +- name: Add package (normal mode) + pkgutil: + name: CSWtop + state: present + register: nm_add_package + +- name: Verify nm_add_package + assert: + that: + - nm_add_package is changed + +- name: Add package again (check_mode) + pkgutil: + name: CSWtop + state: present + check_mode: true + register: cm_add_package_again + +- name: Verify cm_add_package_again + assert: + that: + - cm_add_package_again is not changed + +- name: Add package again (normal mode) + pkgutil: + name: CSWtop + state: present + register: nm_add_package_again + +- name: Verify nm_add_package_again + assert: + that: + - nm_add_package_again is not changed + + +# REMOVE PACKAGE +- name: Remove package (check_mode) + pkgutil: + name: CSWtop + state: absent + check_mode: true + register: cm_remove_package + +- name: Verify cm_remove_package + assert: + that: + - cm_remove_package is changed + +- name: Remove package (normal mode) + pkgutil: + name: CSWtop + state: absent + register: nm_remove_package + +- name: Verify nm_remove_package + assert: + that: + - nm_remove_package is changed + +- name: Remove package again (check_mode) + pkgutil: + name: CSWtop + state: absent + check_mode: true + register: cm_remove_package_again + +- name: Verify cm_remove_package_again + assert: + that: + - cm_remove_package_again is not changed + +- name: Remove package again (normal mode) + pkgutil: + name: CSWtop + state: absent + register: nm_remove_package_again + +- name: Verify nm_remove_package_again + assert: + that: + - nm_remove_package_again is not changed + + +# RESTORE ENVIRONMENT +- name: Reinstall CSWtop + pkgutil: + name: CSWtop + state: present + when: originally_installed is changed diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox/aliases b/ansible_collections/community/general/tests/integration/targets/proxmox/aliases new file mode 100644 index 000000000..5e5957a5c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/proxmox/aliases @@ -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 + +unsupported +proxmox_domain_info +proxmox_group_info +proxmox_user_info +proxmox_storage_info 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 new file mode 100644 index 000000000..22d7fcd29 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml @@ -0,0 +1,579 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2020, Tristan Le Guern +# 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: List domains + proxmox_domain_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_domains is defined + +- name: Retrieve info about pve + proxmox_domain_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 }}" + domain: pve + register: results + +- assert: + that: + - results is not changed + - results.proxmox_domains is defined + - results.proxmox_domains|length == 1 + - results.proxmox_domains[0].type == 'pve' + +- name: List groups + proxmox_group_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_groups is defined + +- name: List users + proxmox_user_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_users is defined + +- name: Retrieve info about api_user using name and domain + proxmox_user_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 }}" + user: "{{ user }}" + domain: "{{ domain }}" + register: results_user_domain + +- assert: + that: + - results_user_domain is not changed + - results_user_domain.proxmox_users is defined + - results_user_domain.proxmox_users|length == 1 + - results_user_domain.proxmox_users[0].domain == "{{ domain }}" + - results_user_domain.proxmox_users[0].user == "{{ user }}" + - results_user_domain.proxmox_users[0].userid == "{{ user }}@{{ domain }}" + +- name: Retrieve info about api_user using userid + proxmox_user_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 }}" + userid: "{{ user }}@{{ domain }}" + register: results_userid + +- assert: + that: + - results_userid is not changed + - results_userid.proxmox_users is defined + - results_userid.proxmox_users|length == 1 + - results_userid.proxmox_users[0].domain == "{{ domain }}" + - results_userid.proxmox_users[0].user == "{{ user }}" + - results_userid.proxmox_users[0].userid == "{{ user }}@{{ domain }}" + +- name: Retrieve info about storage + proxmox_storage_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 }}" + register: results_storage + +- assert: + that: + - results_storage is not changed + - results_storage.proxmox_storages is defined + - results_storage.proxmox_storages|length == 1 + - results_storage.proxmox_storages[0].storage == "{{ storage }}" + +- name: VM creation + tags: [ 'create' ] + block: + - name: Create test vm test-instance + proxmox_kvm: + 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 }}" + storage: "{{ storage }}" + vmid: "{{ from_vmid }}" + name: test-instance + clone: 'yes' + state: present + timeout: 500 + register: results_kvm + + - set_fact: + vmid: "{{ results_kvm.msg.split(' ')[-7] }}" + + - assert: + that: + - results_kvm is changed + - results_kvm.vmid == from_vmid + - results_kvm.msg == "VM test-instance with newid {{ vmid }} cloned from vm with vmid {{ from_vmid }}" + + - pause: + seconds: 30 + +- name: VM start + tags: [ 'start' ] + block: + - name: Start test VM + proxmox_kvm: + 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 }}" + vmid: "{{ vmid }}" + state: started + register: results_action_start + + - assert: + that: + - results_action_start is changed + - results_action_start.status == 'stopped' + - results_action_start.vmid == {{ vmid }} + - results_action_start.msg == "VM {{ vmid }} started" + + - pause: + seconds: 90 + + - name: Try to start test VM again + proxmox_kvm: + 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 }}" + vmid: "{{ vmid }}" + state: started + register: results_action_start_again + + - assert: + that: + - results_action_start_again is not changed + - results_action_start_again.status == 'running' + - results_action_start_again.vmid == {{ vmid }} + - results_action_start_again.msg == "VM {{ vmid }} is already running" + + - name: Check current status + proxmox_kvm: + 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 }}" + vmid: "{{ vmid }}" + state: current + register: results_action_current + + - assert: + that: + - results_action_current is not changed + - results_action_current.status == 'running' + - results_action_current.vmid == {{ vmid }} + - results_action_current.msg == "VM test-instance with vmid = {{ vmid }} is running" + +- name: VM add/change/delete NIC + tags: [ 'nic' ] + block: + - name: Add NIC to test VM + proxmox_nic: + 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 }}" + vmid: "{{ vmid }}" + state: present + interface: net5 + bridge: vmbr0 + tag: 42 + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 updated on VM with vmid {{ vmid }}" + + - name: Update NIC no changes + proxmox_nic: + 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 }}" + vmid: "{{ vmid }}" + state: present + interface: net5 + bridge: vmbr0 + tag: 42 + register: results + + - assert: + that: + - results is not changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 unchanged on VM with vmid {{ vmid }}" + + - name: Update NIC with changes + proxmox_nic: + 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 }}" + vmid: "{{ vmid }}" + state: present + interface: net5 + bridge: vmbr0 + tag: 24 + firewall: true + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 updated on VM with vmid {{ vmid }}" + + - name: Delete NIC + proxmox_nic: + 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 }}" + vmid: "{{ vmid }}" + state: absent + interface: net5 + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Nic net5 deleted on VM with vmid {{ vmid }}" + +- name: Create new disk in VM + tags: ['create_disk'] + block: + - name: Add new disk (without force) to VM + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + storage: "{{ storage }}" + size: 1 + state: present + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} created in VM {{ vmid }}" + + - name: Try add disk again with same options (expect no-op) + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + storage: "{{ storage }}" + size: 1 + state: present + register: results + + - assert: + that: + - results is not changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} is up to date in VM {{ vmid }}" + + - name: Add new disk replacing existing disk (detach old and leave unused) + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + storage: "{{ storage }}" + size: 2 + create: forced + state: present + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} created in VM {{ vmid }}" + +- name: Update existing disk in VM + tags: ['update_disk'] + block: + - name: Update disk configuration + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + backup: false + ro: true + aio: native + state: present + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} updated in VM {{ vmid }}" + +- name: Grow existing disk in VM + tags: ['grow_disk'] + block: + - name: Increase disk size + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + size: +1G + state: resized + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} resized in VM {{ vmid }}" + +- name: Detach disk and leave it unused + tags: ['detach_disk'] + block: + - name: Detach disk + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + state: detached + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} detached from VM {{ vmid }}" + +- name: Move disk to another storage or another VM + tags: ['move_disk'] + block: + - name: Move disk to another storage inside same VM + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + target_storage: "{{ target_storage }}" + format: "{{ target_format }}" + state: moved + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} moved from VM {{ vmid }} storage {{ results.storage }}" + + - name: Move disk to another VM (same storage) + proxmox_disk: + 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) }}" + vmid: "{{ vmid }}" + disk: "{{ disk }}" + target_vmid: "{{ target_vm }}" + target_disk: "{{ target_disk }}" + state: moved + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ vmid }} + - results.msg == "Disk {{ disk }} moved from VM {{ vmid }} storage {{ results.storage }}" + + +- name: Remove disk permanently + tags: ['remove_disk'] + block: + - name: Remove disk + proxmox_disk: + 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) }}" + vmid: "{{ target_vm }}" + disk: "{{ target_disk }}" + state: absent + register: results + + - assert: + that: + - results is changed + - results.vmid == {{ target_vm }} + - results.msg == "Disk {{ target_disk }} removed from VM {{ target_vm }}" + +- name: VM stop + tags: [ 'stop' ] + block: + - name: Stop test VM + proxmox_kvm: + 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 }}" + vmid: "{{ vmid }}" + state: stopped + register: results_action_stop + + - assert: + that: + - results_action_stop is changed + - results_action_stop.status == 'running' + - results_action_stop.vmid == {{ vmid }} + - results_action_stop.msg == "VM {{ vmid }} is shutting down" + + - pause: + seconds: 5 + + - name: Check current status again + proxmox_kvm: + 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 }}" + vmid: "{{ vmid }}" + state: current + register: results_action_current + + - assert: + that: + - results_action_current is not changed + - results_action_current.status == 'stopped' + - results_action_current.vmid == {{ vmid }} + - results_action_current.msg == "VM test-instance with vmid = {{ vmid }} is stopped" + +- name: VM destroy + tags: [ 'destroy' ] + block: + - name: Destroy test VM + proxmox_kvm: + 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 }}" + proxmox_default_behavior: "no_defaults" + node: "{{ node }}" + vmid: "{{ vmid }}" + state: absent + register: results_kvm_destroy + + - assert: + that: + - results_kvm_destroy is changed + - results_kvm_destroy.vmid == {{ vmid }} + - results_kvm_destroy.msg == "VM {{ vmid }} removed" diff --git a/ansible_collections/community/general/tests/integration/targets/python_requirements_info/aliases b/ansible_collections/community/general/tests/integration/targets/python_requirements_info/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/python_requirements_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 + +azp/posix/2 diff --git a/ansible_collections/community/general/tests/integration/targets/python_requirements_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/python_requirements_info/tasks/main.yml new file mode 100644 index 000000000..24a7d1366 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/python_requirements_info/tasks/main.yml @@ -0,0 +1,45 @@ +--- +#################################################################### +# 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: run python_requirements_info module + python_requirements_info: + register: basic_info + +- name: ensure python_requirements_info returns desired info + assert: + that: + - "'python' in basic_info" + - "'python_version' in basic_info" + - basic_info.python_version_info == ansible_python.version + +- name: run python_requirements_info module + python_requirements_info: + dependencies: + - notreal<1 + - pip>1 + register: dep_info + +- name: ensure python_requirements_info returns desired info + assert: + that: + - "'installed' in dep_info.valid.pip" + - "'notreal' in dep_info.not_found" + +- name: wrong specs + python_requirements_info: + dependencies: + - ansible< + register: wrong_spec1 + ignore_errors: true + +- name: ensure wrong specs return error + assert: + that: + - wrong_spec1 is failed diff --git a/ansible_collections/community/general/tests/integration/targets/read_csv/aliases b/ansible_collections/community/general/tests/integration/targets/read_csv/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/read_csv/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/read_csv/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/read_csv/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/read_csv/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/read_csv/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/read_csv/tasks/main.yml new file mode 100644 index 000000000..c09349dd5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/read_csv/tasks/main.yml @@ -0,0 +1,176 @@ +--- +#################################################################### +# 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 + +# Create basic CSV file +- name: Create unique CSV file + copy: + content: &users_content | + name,uid,gid,gecos + dag,500,500,Dag Wieërs + jeroen,501,500,Jeroen Hoekx + dest: "{{ remote_tmp_dir }}/users_unique.csv" + +# Read a CSV file and access user 'dag' +- name: Read users from CSV file and return a dictionary + read_csv: + path: "{{ remote_tmp_dir }}/users_unique.csv" + key: name + register: users_unique + +- assert: + that: + - users_unique.dict.dag.name == 'dag' + - users_unique.dict.dag.gecos == 'Dag Wieërs' + - users_unique.dict.dag.uid == '500' + - users_unique.dict.dag.gid == '500' + - users_unique.dict.jeroen.name == 'jeroen' + - users_unique.dict.jeroen.gecos == 'Jeroen Hoekx' + - users_unique.dict.jeroen.uid == '501' + - users_unique.dict.jeroen.gid == '500' + +# Read a CSV file and access the first item +- name: Read users from CSV file and return a list + read_csv: + path: "{{ remote_tmp_dir }}/users_unique.csv" + register: users_unique + +- assert: + that: + - users_unique.list.0.name == 'dag' + - users_unique.list.0.gecos == 'Dag Wieërs' + - users_unique.list.0.uid == '500' + - users_unique.list.0.gid == '500' + - users_unique.list.1.name == 'jeroen' + - users_unique.list.1.gecos == 'Jeroen Hoekx' + - users_unique.list.1.uid == '501' + - users_unique.list.1.gid == '500' + + +# Create basic CSV file using semi-colon +- name: Create non-unique CSV file using semi-colon + copy: + content: | + name;uid;gid;gecos + dag;500;500;Dag Wieërs + jeroen;501;500;Jeroen Hoekx + dag;502;500;Dag Wieers + dest: "{{ remote_tmp_dir }}/users_nonunique.csv" + +# Read a CSV file and access user 'dag' +- name: Read users from CSV file and return a dictionary + read_csv: + path: "{{ remote_tmp_dir }}/users_nonunique.csv" + key: name + unique: false + delimiter: ';' + register: users_nonunique + +- assert: + that: + - users_nonunique.dict.dag.name == 'dag' + - users_nonunique.dict.dag.gecos == 'Dag Wieers' + - users_nonunique.dict.dag.uid == '502' + - users_nonunique.dict.dag.gid == '500' + - users_nonunique.dict.jeroen.name == 'jeroen' + - users_nonunique.dict.jeroen.gecos == 'Jeroen Hoekx' + - users_nonunique.dict.jeroen.uid == '501' + - users_nonunique.dict.jeroen.gid == '500' + + +# Read a CSV file using an non-existing dialect +- name: Read users from CSV file and return a dictionary + read_csv: + path: "{{ remote_tmp_dir }}/users_nonunique.csv" + dialect: placebo + register: users_placebo + ignore_errors: true + +- assert: + that: + - users_placebo is failed + - users_placebo.msg == "Dialect 'placebo' is not supported by your version of python." + + +# Create basic CSV file without header +- name: Create unique CSV file without header + copy: + content: | + dag,500,500,Dag Wieërs + jeroen,501,500,Jeroen Hoekx + dest: "{{ remote_tmp_dir }}/users_noheader.csv" + +# Read a CSV file and access user 'dag' +- name: Read users from CSV file and return a dictionary + read_csv: + path: "{{ remote_tmp_dir }}/users_noheader.csv" + key: name + fieldnames: name,uid,gid,gecos + register: users_noheader + +- assert: + that: + - users_noheader.dict.dag.name == 'dag' + - users_noheader.dict.dag.gecos == 'Dag Wieërs' + - users_noheader.dict.dag.uid == '500' + - users_noheader.dict.dag.gid == '500' + - users_noheader.dict.jeroen.name == 'jeroen' + - users_noheader.dict.jeroen.gecos == 'Jeroen Hoekx' + - users_noheader.dict.jeroen.uid == '501' + - users_noheader.dict.jeroen.gid == '500' + + +# Create broken file +- name: Create unique CSV file + copy: + content: | + name,uid,gid,gecos + dag,500,500,Dag Wieërs + jeroen,501,500,"Jeroen"Hoekx" + dest: "{{ remote_tmp_dir }}/users_broken.csv" + +# Read a broken CSV file using strict +- name: Read users from a broken CSV file + read_csv: + path: "{{ remote_tmp_dir }}/users_broken.csv" + key: name + strict: true + register: users_broken + ignore_errors: true + +- assert: + that: + - users_broken is failed + - "'Unable to process file' in users_broken.msg" + +# Create basic CSV file with BOM +- name: Create unique CSV file with BOM + copy: + content: "{{ bom + content }}" + dest: "{{ remote_tmp_dir }}/users_bom.csv" + vars: + content: *users_content + bom: "{{ '\ufeff' }}" + + # Read a CSV file and access the first item +- name: Read users from CSV file and return a list + read_csv: + path: "{{ remote_tmp_dir }}/users_bom.csv" + register: users_bom + +- assert: + that: + - users_bom.list.0.name == 'dag' + - users_bom.list.0.gecos == 'Dag Wieërs' + - users_bom.list.0.uid == '500' + - users_bom.list.0.gid == '500' + - users_bom.list.1.name == 'jeroen' + - users_bom.list.1.gecos == 'Jeroen Hoekx' + - users_bom.list.1.uid == '501' + - users_bom.list.1.gid == '500' diff --git a/ansible_collections/community/general/tests/integration/targets/redis_info/aliases b/ansible_collections/community/general/tests/integration/targets/redis_info/aliases new file mode 100644 index 000000000..1f1c4baf7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/redis_info/aliases @@ -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 + +azp/posix/1 +destructive +skip/aix +skip/osx +skip/macos +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/redis_info/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/redis_info/defaults/main.yml new file mode 100644 index 000000000..56e9c4386 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/redis_info/defaults/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 + +redis_password: PASS +master_port: 6379 +replica_port: 6380 diff --git a/ansible_collections/community/general/tests/integration/targets/redis_info/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/redis_info/meta/main.yml new file mode 100644 index 000000000..cd516fd23 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/redis_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_redis_replication diff --git a/ansible_collections/community/general/tests/integration/targets/redis_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/redis_info/tasks/main.yml new file mode 100644 index 000000000..4a11de365 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/redis_info/tasks/main.yml @@ -0,0 +1,48 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2020, Pavlo Bashynskyi (@levonet) +# 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: redis_info - connect to master with default host/port + community.general.redis_info: + login_password: "{{ redis_password }}" + register: result + +- assert: + that: + - result is not changed + - result.info is defined + - result.info.tcp_port == master_port + - result.info.role == 'master' + +- name: redis_info - connect to master (check) + community.general.redis_info: + login_host: 127.0.0.1 + login_port: "{{ master_port }}" + login_password: "{{ redis_password }}" + check_mode: true + register: result + +- assert: + that: + - result is not changed + - result.info is defined + - result.info.tcp_port == master_port + - result.info.role == 'master' + +- name: redis_info - connect to replica + community.general.redis_info: + login_port: "{{ replica_port }}" + login_password: "{{ redis_password }}" + register: result + +- assert: + that: + - result is not changed + - result.info is defined + - result.info.tcp_port == replica_port + - result.info.role == 'slave' diff --git a/ansible_collections/community/general/tests/integration/targets/rundeck/aliases b/ansible_collections/community/general/tests/integration/targets/rundeck/aliases new file mode 100644 index 000000000..3cf494c4c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/rundeck/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 +destructive +skip/aix +skip/osx +skip/macos +skip/windows +skip/freebsd +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/rundeck/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/rundeck/defaults/main.yml new file mode 100644 index 000000000..4d7ea3146 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/rundeck/defaults/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 + +rundeck_url: http://localhost:4440 +rundeck_api_version: 39 +rundeck_job_id: 3b8a6e54-69fb-42b7-b98f-f82e59238478 diff --git a/ansible_collections/community/general/tests/integration/targets/rundeck/files/test_job.yaml b/ansible_collections/community/general/tests/integration/targets/rundeck/files/test_job.yaml new file mode 100644 index 000000000..baa852ecc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/rundeck/files/test_job.yaml @@ -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 + +- defaultTab: nodes + description: '' + executionEnabled: true + id: 3b8a6e54-69fb-42b7-b98f-f82e59238478 + loglevel: INFO + name: test_job + nodeFilterEditable: false + options: + - label: Exit Code + name: exit_code + value: '0' + - label: Sleep + name: sleep + value: '1' + plugins: + ExecutionLifecycle: null + scheduleEnabled: true + sequence: + commands: + - exec: sleep $RD_OPTION_SLEEP && echo "Test done!" && exit $RD_OPTION_EXIT_CODE + keepgoing: false + strategy: node-first + uuid: 3b8a6e54-69fb-42b7-b98f-f82e59238478 diff --git a/ansible_collections/community/general/tests/integration/targets/rundeck/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/rundeck/meta/main.yml new file mode 100644 index 000000000..c125e4046 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/rundeck/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_rundeck diff --git a/ansible_collections/community/general/tests/integration/targets/rundeck/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/rundeck/tasks/main.yml new file mode 100644 index 000000000..e42780b9b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/rundeck/tasks/main.yml @@ -0,0 +1,127 @@ +--- +#################################################################### +# 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: Generate a Rundeck API Token + ansible.builtin.command: java -jar {{ rdeck_base }}/rundeck-cli.jar tokens create -u admin -d 24h -r admin + environment: + RD_URL: "{{ rundeck_url }}" + RD_USER: admin + RD_PASSWORD: admin + register: rundeck_api_token + +- name: Create a Rundeck project + community.general.rundeck_project: + name: "test_project" + api_version: "{{ rundeck_api_version }}" + url: "{{ rundeck_url }}" + token: "{{ rundeck_api_token.stdout_lines[-1] }}" + state: present + +- name: Copy test_job definition to /tmp + copy: + src: test_job.yaml + dest: /tmp/test_job.yaml + +- name: Create Rundeck job Test + ansible.builtin.command: java -jar {{ rdeck_base }}/rundeck-cli.jar jobs load -f /tmp/test_job.yaml -F yaml -p test_project + environment: + RD_URL: "{{ rundeck_url }}" + RD_USER: admin + RD_PASSWORD: admin + +- name: Wrong Rundeck API Token + community.general.rundeck_job_run: + url: "{{ rundeck_url }}" + api_version: "{{ rundeck_api_version }}" + api_token: wrong_token + job_id: "{{ rundeck_job_id }}" + ignore_errors: true + register: rundeck_job_run_wrong_token + +- name: Assert that Rundeck authorization failed + ansible.builtin.assert: + that: + - rundeck_job_run_wrong_token.msg == "Token authorization failed" + +- name: Success run Rundeck job test_job + community.general.rundeck_job_run: + url: "{{ rundeck_url }}" + api_version: "{{ rundeck_api_version }}" + api_token: "{{ rundeck_api_token.stdout_lines[-1] }}" + job_id: "{{ rundeck_job_id }}" + register: rundeck_job_run_success + +- name: Assert that Rundeck job test_job runs successfully + ansible.builtin.assert: + that: + - rundeck_job_run_success.execution_info.status == "succeeded" + +- name: Fail run Rundeck job test_job + community.general.rundeck_job_run: + url: "{{ rundeck_url }}" + api_version: "{{ rundeck_api_version }}" + api_token: "{{ rundeck_api_token.stdout_lines[-1] }}" + job_id: "{{ rundeck_job_id }}" + job_options: + exit_code: "1" + ignore_errors: true + register: rundeck_job_run_fail + +- name: Assert that Rundeck job test_job failed + ansible.builtin.assert: + that: + - rundeck_job_run_fail.execution_info.status == "failed" + +- name: Abort run Rundeck job test_job due timeout + community.general.rundeck_job_run: + url: "{{ rundeck_url }}" + api_version: "{{ rundeck_api_version }}" + api_token: "{{ rundeck_api_token.stdout_lines[-1] }}" + job_id: "{{ rundeck_job_id }}" + job_options: + sleep: "5" + wait_execution_timeout: 2 + abort_on_timeout: true + ignore_errors: true + register: rundeck_job_run_aborted + +- name: Assert that Rundeck job test_job is aborted + ansible.builtin.assert: + that: + - rundeck_job_run_aborted.execution_info.status == "aborted" + +- name: Fire-and-forget run Rundeck job test_job + community.general.rundeck_job_run: + url: "{{ rundeck_url }}" + api_version: "{{ rundeck_api_version }}" + api_token: "{{ rundeck_api_token.stdout_lines[-1] }}" + job_id: "{{ rundeck_job_id }}" + job_options: + sleep: "5" + wait_execution: false + register: rundeck_job_run_forget + +- name: Assert that Rundeck job test_job is running + ansible.builtin.assert: + that: + - rundeck_job_run_forget.execution_info.status == "running" + +- name: Get Rundeck job test_job executions info + community.general.rundeck_job_executions_info: + url: "{{ rundeck_url }}" + api_version: "{{ rundeck_api_version }}" + api_token: "{{ rundeck_api_token.stdout_lines[-1] }}" + job_id: "{{ rundeck_job_id }}" + register: rundeck_job_executions_info + +- name: Assert that Rundeck job executions info has 4 registers + ansible.builtin.assert: + that: + - rundeck_job_executions_info.paging.total | int == 4 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/defaults/main.yml new file mode 100644 index 000000000..dfa104bdd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/defaults/main.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 + +# Below information has been taken from https://developer.scaleway.com/#servers +scaleway_image_id: 6a601340-19c1-4ca7-9c1c-0704bcc9f5fe +scaleway_organization: '{{ scw_org }}' +scaleway_region: ams1 +scaleway_commerial_type: START1-S +scaleway_name: scaleway_compute_test +first_server_name: scaleway_compute_test_first +second_server_name: scaleway_compute_test_second diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/ip.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/ip.yml new file mode 100644 index 000000000..094176ce8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/ip.yml @@ -0,0 +1,206 @@ +--- +# 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 server with no IP (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + + register: server_creation_absent_check_task + +- debug: var=server_creation_absent_check_task + +- assert: + that: + - server_creation_absent_check_task is success + - server_creation_absent_check_task is changed + +- name: Create a server + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_creation_absent_task + +- debug: var=server_creation_absent_task + +- assert: + that: + - server_creation_absent_task is success + - server_creation_absent_task is changed + +- name: Create a server (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_creation_absent_confirmation_task + +- debug: var=server_creation_absent_confirmation_task + +- assert: + that: + - server_creation_absent_confirmation_task is success + - server_creation_absent_confirmation_task is not changed + +# Add a dynamic IP to the instance + +- name: Patch server tags (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: dynamic + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + + register: ip_patching_check_task + +- debug: var=ip_patching_check_task + +- assert: + that: + - ip_patching_check_task is success + - ip_patching_check_task is changed + +- name: Patch server tags + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: dynamic + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + register: ip_patching_task + +- debug: var=ip_patching_task + +- assert: + that: + - ip_patching_task is success + - ip_patching_task is changed + +- name: Patch server tags (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: dynamic + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: ip_patching_confirmation_task + +- debug: var=ip_patching_confirmation_task + +- assert: + that: + - ip_patching_confirmation_task is success + - ip_patching_confirmation_task is not changed + +# Remove dynamic IP + +- name: Patch server tags (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + + register: remove_ip_check_task + +- debug: var=remove_ip_check_task + +- assert: + that: + - remove_ip_check_task is success + - remove_ip_check_task is changed + +- name: Patch server tags + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: remove_ip_task + +- debug: var=remove_ip_task + +- assert: + that: + - remove_ip_task is success + - remove_ip_task is changed + +- name: Patch server tags (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + public_ip: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: remove_ip_confirmation_task + +- debug: var=remove_ip_confirmation_task + +- assert: + that: + - remove_ip_confirmation_task is success + - remove_ip_confirmation_task is not changed + +- name: Destroy it + scaleway_compute: + name: '{{ scaleway_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_destroy_task + +- debug: var=server_destroy_task + +- assert: + that: + - server_destroy_task is success + - server_destroy_task is changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/main.yml new file mode 100644 index 000000000..eca689b40 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/main.yml @@ -0,0 +1,14 @@ +--- +#################################################################### +# 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 + +- include_tasks: state.yml +- include_tasks: ip.yml +- include_tasks: security_group.yml +- include_tasks: pagination.yml 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 new file mode 100644 index 000000000..5a674b801 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.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: Create a first server + scaleway_compute: + name: '{{ first_server_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + +- name: Create a second server + scaleway_compute: + name: '{{ second_server_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + +- name: Get server informations of the first page + scaleway_server_info: + region: par1 + query_parameters: + per_page: 1 + page: 1 + register: first_page + +- debug: var=first_page + +- assert: + that: + - first_page is success + +- name: Get server informations of the second page + scaleway_server_info: + region: par1 + query_parameters: + per_page: 1 + page: 2 + register: second_page + +- debug: var=second_page + +- assert: + that: + - second_page is success + +- assert: + that: + - first_page.scaleway_server_info[0].id != second_page.scaleway_server_info[0].id + +- name: Delete first server + scaleway_compute: + name: '{{ first_server_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + +- name: Delete second server + scaleway_compute: + name: '{{ second_server_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/security_group.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/security_group.yml new file mode 100644 index 000000000..59f81e6af --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/security_group.yml @@ -0,0 +1,152 @@ +--- +# 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 scaleway security_group + scaleway_security_group: + state: present + region: '{{ scaleway_region }}' + name: test_compute + description: test_compute + organization: '{{ scaleway_organization }}' + stateful: true + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group + +- debug: var=security_group + +- block: + - name: Create a server with security_group (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + + register: server_creation_check_task + + - debug: var=server_creation_check_task + + - assert: + that: + - server_creation_check_task is success + - server_creation_check_task is changed + + - name: Create a server + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + wait: true + + register: server_creation_task + + - debug: var=server_creation_task + + - assert: + that: + - server_creation_task is success + - server_creation_task is changed + + - name: Create a server with security_group (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + wait: true + + register: server_creation_confirmation_task + + - debug: var=server_creation_confirmation_task + + - assert: + that: + - server_creation_confirmation_task is success + - server_creation_confirmation_task is not changed + + - name: Keep current security_group (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + wait: true + + register: server_creation_confirmation_task + + - debug: var=server_creation_confirmation_task + + - assert: + that: + - server_creation_confirmation_task is success + - server_creation_confirmation_task is not changed + + - name: Keep current security_group + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_creation_confirmation_task + + - debug: var=server_creation_confirmation_task + + - assert: + that: + - server_creation_confirmation_task is success + - server_creation_confirmation_task is not changed + + always: + - name: Destroy it + scaleway_compute: + name: '{{ scaleway_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_destroy_task + + - debug: var=server_destroy_task + + - assert: + that: + - server_destroy_task is success + - server_destroy_task is changed + + - name: Create a scaleway security_group + scaleway_security_group: + state: absent + region: '{{ scaleway_region }}' + name: test_compute + description: test_compute + organization: '{{ scaleway_organization }}' + stateful: true + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/state.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/state.yml new file mode 100644 index 000000000..b3f256762 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/state.yml @@ -0,0 +1,392 @@ +--- +# 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 server (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + + register: server_creation_check_task + +- debug: var=server_creation_check_task + +- assert: + that: + - server_creation_check_task is success + - server_creation_check_task is changed + +- name: Create a server + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_creation_task + +- debug: var=server_creation_task + +- assert: + that: + - server_creation_task is success + - server_creation_task is changed + +- name: Create a server (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_creation_confirmation_task + +- debug: var=server_creation_confirmation_task + +- assert: + that: + - server_creation_confirmation_task is success + - server_creation_confirmation_task is not changed + +- name: Patch server tags (Check) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + tags: + - test + - www + register: server_patching_check_task + +- debug: var=server_patching_check_task + +- assert: + that: + - server_patching_check_task is success + - server_patching_check_task is changed + +- name: Patch server tags + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_patching_task + +- debug: var=server_patching_task + +- assert: + that: + - server_patching_task is success + - server_patching_task is changed + +- name: Patch server tags (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_patching_confirmation_task + +- debug: var=server_patching_confirmation_task + +- assert: + that: + - server_patching_confirmation_task is success + - server_patching_confirmation_task is not changed + +- name: Run it (Check mode) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: running + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + tags: + - test + - www + register: server_run_check_task + +- debug: var=server_run_check_task + +- assert: + that: + - server_run_check_task is success + - server_run_check_task is changed + +- name: Run it + scaleway_compute: + name: '{{ scaleway_name }}' + state: running + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_run_task + +- debug: var=server_run_task + +- assert: + that: + - server_run_task is success + - server_run_task is changed + +- name: Run it + scaleway_compute: + name: '{{ scaleway_name }}' + state: running + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_run_confirmation_task + +- debug: var=server_run_confirmation_task + +- assert: + that: + - server_run_confirmation_task is success + - server_run_confirmation_task is not changed + +- name: Reboot it (Check mode) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: restarted + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + tags: + - test + - www + register: server_reboot_check_task + +- debug: var=server_reboot_check_task + +- assert: + that: + - server_reboot_check_task is success + - server_reboot_check_task is changed + +- name: Reboot it + scaleway_compute: + name: '{{ scaleway_name }}' + state: restarted + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_reboot_task + +- debug: var=server_reboot_task + +- assert: + that: + - server_reboot_task is success + - server_reboot_task is changed + +- name: Stop it (Check mode) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: stopped + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + tags: + - test + - www + register: server_stop_check_task + +- debug: var=server_stop_check_task + +- assert: + that: + - server_stop_check_task is success + - server_stop_check_task is changed + +- name: Stop it + scaleway_compute: + name: '{{ scaleway_name }}' + state: stopped + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_stop_task + +- debug: var=server_stop_task + +- assert: + that: + - server_stop_task is success + - server_stop_task is changed + +- name: Stop it (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: stopped + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_stop_confirmation_task + +- debug: var=server_stop_confirmation_task + +- assert: + that: + - server_stop_confirmation_task is success + - server_stop_confirmation_task is not changed + +- name: Destroy it (Check mode) + check_mode: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + tags: + - test + - www + register: server_destroy_check_task + +- debug: var=server_destroy_check_task + +- assert: + that: + - server_destroy_check_task is success + - server_destroy_check_task is changed + +- name: Destroy it + scaleway_compute: + name: '{{ scaleway_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_destroy_task + +- debug: var=server_destroy_task + +- assert: + that: + - server_destroy_task is success + - server_destroy_task is changed + +- name: Destroy it (Confirmation) + scaleway_compute: + name: '{{ scaleway_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + tags: + - test + - www + register: server_destroy_confirmation_task + +- debug: var=server_destroy_confirmation_task + +- assert: + that: + - server_destroy_confirmation_task is success + - server_destroy_confirmation_task is not changed + +- name: Testing for unauthorized organization + ignore_errors: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: '{{ scaleway_image_id }}' + organization: this-organization-does-not-exists + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + register: unauthorized_organization_task + +- debug: var=unauthorized_organization_task + +- assert: + that: + - unauthorized_organization_task is not success + - unauthorized_organization_task is not changed + +- name: Testing for unexisting image + ignore_errors: true + scaleway_compute: + name: '{{ scaleway_name }}' + state: present + image: this-image-does-not-exists + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + register: unexisting_image_check + +- debug: var=unexisting_image_check + +- assert: + that: + - unexisting_image_check is not success + - unexisting_image_check is not changed + - unexisting_image_check.msg == "Error in getting image this-image-does-not-exists on https://cp-{{scaleway_region}}.scaleway.com" diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_container/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container/defaults/main.yml new file mode 100644 index 000000000..01b8719fc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container/defaults/main.yml @@ -0,0 +1,18 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +container_namespace_name: cn-ansible-test +name: cn-ansible-test +description: Container used for testing scaleway_container ansible module +updated_description: Container used for testing scaleway_container ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value +updated_secret_environment_variables: + MY_SECRET_VAR: my_other_secret_value +image: rg.fr-par.scw.cloud/namespace-ansible-ci/nginx:latest +port: 80 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container/tasks/main.yml new file mode 100644 index 000000000..d0bf4206f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container/tasks/main.yml @@ -0,0 +1,290 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 container_namespace + community.general.scaleway_container_namespace: + state: present + name: '{{ container_namespace_name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: integration_container_namespace + +- name: Create a container (Check) + check_mode: true + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + register: cn_creation_check_task + +- ansible.builtin.debug: + var: cn_creation_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_creation_check_task is success + - cn_creation_check_task is changed + +- name: Create container + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + register: cn_creation_task + +- ansible.builtin.debug: + var: cn_creation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_creation_task is success + - cn_creation_task is changed + - cn_creation_task.container.status in ["created", "ready"] + +- name: Create container (Confirmation) + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + register: cn_creation_confirmation_task + +- ansible.builtin.debug: + var: cn_creation_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_creation_confirmation_task is success + - cn_creation_confirmation_task is not changed + - cn_creation_confirmation_task.container.status in ["created", "ready"] + +- name: Update container (Check) + check_mode: true + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + register: cn_update_check_task + +- ansible.builtin.debug: + var: cn_update_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_check_task is success + - cn_update_check_task is changed + +- name: Update container + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + register: cn_update_task + +- ansible.builtin.debug: + var: cn_update_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_task is success + - cn_update_task is changed + - cn_update_task.container.status in ["created", "ready"] + +- name: Update container (Confirmation) + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + register: cn_update_confirmation_task + +- ansible.builtin.debug: + var: cn_update_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_confirmation_task is success + - cn_update_confirmation_task is not changed + - cn_update_confirmation_task.container.status in ["created", "ready"] + +- name: Update container secret variables (Check) + check_mode: true + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + port: '{{ port }}' + register: cn_update_secret_check_task + +- ansible.builtin.debug: + var: cn_update_secret_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_secret_check_task is success + - cn_update_secret_check_task is changed + +- name: Update container secret variables + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + port: '{{ port }}' + register: cn_update_secret_task + +- ansible.builtin.debug: + var: cn_update_secret_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_secret_task is success + - cn_update_secret_task is changed + - cn_update_secret_task.container.status in ["created", "ready"] + - "'hashed_value' in cn_update_secret_task.container.secret_environment_variables[0]" + +- name: Update container secret variables (Confirmation) + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + port: '{{ port }}' + register: cn_update_secret_confirmation_task + +- ansible.builtin.debug: + var: cn_update_secret_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_secret_confirmation_task is success + - cn_update_secret_confirmation_task is not changed + - cn_update_secret_confirmation_task.container.status == "ready" + - "'hashed_value' in cn_update_secret_confirmation_task.container.secret_environment_variables[0]" + +- name: Delete container (Check) + check_mode: true + community.general.scaleway_container: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + register: cn_deletion_check_task + +- ansible.builtin.debug: + var: cn_deletion_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_deletion_check_task is success + - cn_deletion_check_task is changed + +- name: Delete container + community.general.scaleway_container: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + register: cn_deletion_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_deletion_task is success + - cn_deletion_task is changed + +- name: Delete container (Confirmation) + community.general.scaleway_container: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + register: cn_deletion_confirmation_task + +- ansible.builtin.debug: + var: cn_deletion_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_deletion_confirmation_task is success + - cn_deletion_confirmation_task is not changed + +- name: Delete container namespace + community.general.scaleway_container_namespace: + state: absent + name: '{{ container_namespace_name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/defaults/main.yml new file mode 100644 index 000000000..f3dadf71a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/defaults/main.yml @@ -0,0 +1,16 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +container_namespace_name: cn-ansible-test +name: cn-ansible-test +description: Container used for testing scaleway_container_info ansible module +updated_description: Container used for testing scaleway_container_info ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value +image: rg.fr-par.scw.cloud/namespace-ansible-ci/nginx:latest +port: 80 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/tasks/main.yml new file mode 100644 index 000000000..9f9fe401c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_info/tasks/main.yml @@ -0,0 +1,63 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 container_namespace + community.general.scaleway_container_namespace: + state: present + name: '{{ container_namespace_name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: integration_container_namespace + +- name: Create container + community.general.scaleway_container: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + port: '{{ port }}' + +- name: Get container info + community.general.scaleway_container_info: + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + register: cn_info_task + +- ansible.builtin.debug: + var: cn_info_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_info_task is success + - cn_info_task is not changed + +- name: Delete container + community.general.scaleway_container: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_container_namespace.container_namespace.id }}' + registry_image: '{{ image }}' + +- name: Delete container namespace + community.general.scaleway_container_namespace: + state: absent + name: '{{ container_namespace_name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/defaults/main.yml new file mode 100644 index 000000000..876f8b7a6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/defaults/main.yml @@ -0,0 +1,15 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +name: cn-ansible-test +description: Container namespace used for testing scaleway_container_namespace ansible module +updated_description: Container namespace used for testing scaleway_container_namespace ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value +updated_secret_environment_variables: + MY_SECRET_VAR: my_other_secret_value \ No newline at end of file diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/tasks/main.yml new file mode 100644 index 000000000..73e43ff15 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace/tasks/main.yml @@ -0,0 +1,255 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 container namespace (Check) + check_mode: true + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: cn_creation_check_task + +- ansible.builtin.debug: + var: cn_creation_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_creation_check_task is success + - cn_creation_check_task is changed + +- name: Create container_namespace + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: cn_creation_task + +- ansible.builtin.debug: + var: cn_creation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_creation_task is success + - cn_creation_task is changed + - cn_creation_task.container_namespace.status == "ready" + +- name: Create container namespace (Confirmation) + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: cn_creation_confirmation_task + +- ansible.builtin.debug: + var: cn_creation_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_creation_confirmation_task is success + - cn_creation_confirmation_task is not changed + - cn_creation_confirmation_task.container_namespace.status == "ready" + +- name: Update container namespace (Check) + check_mode: true + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: cn_update_check_task + +- ansible.builtin.debug: + var: cn_update_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_check_task is success + - cn_update_check_task is changed + +- name: Update container namespace + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: cn_update_task + +- ansible.builtin.debug: + var: cn_update_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_task is success + - cn_update_task is changed + - cn_update_task.container_namespace.status == "ready" + +- name: Update container namespace (Confirmation) + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: cn_update_confirmation_task + +- ansible.builtin.debug: + var: cn_update_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_confirmation_task is success + - cn_update_confirmation_task is not changed + - cn_update_confirmation_task.container_namespace.status == "ready" + +- name: Update container namespace secret variables (Check) + check_mode: true + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: cn_update_secret_check_task + +- ansible.builtin.debug: + var: cn_update_secret_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_secret_check_task is success + - cn_update_secret_check_task is changed + +- name: Update container namespace secret variables + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: cn_update_secret_task + +- ansible.builtin.debug: + var: cn_update_secret_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_secret_task is success + - cn_update_secret_task is changed + - cn_update_secret_task.container_namespace.status == "ready" + - "'hashed_value' in cn_update_secret_task.container_namespace.secret_environment_variables[0]" + +- name: Update container namespace secret variables (Confirmation) + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: cn_update_secret_congfirmation_task + +- ansible.builtin.debug: + var: cn_update_secret_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_update_secret_confirmation_task is success + - cn_update_secret_confirmation_task is not changed + - cn_update_secret_confirmation_task.container_namespace.status == "ready" + - "'hashed_value' in cn_update_secret_confirmation_task.container_namespace.secret_environment_variables[0]" + +- name: Delete container namespace (Check) + check_mode: true + community.general.scaleway_container_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: cn_deletion_check_task + +- ansible.builtin.debug: + var: cn_deletion_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_deletion_check_task is success + - cn_deletion_check_task is changed + +- name: Delete container namespace + community.general.scaleway_container_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: cn_deletion_task + +- ansible.builtin.debug: + var: cn_deletion_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_deletion_task is success + - cn_deletion_task is changed + +- name: Delete container namespace (Confirmation) + community.general.scaleway_container_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: cn_deletion_confirmation_task + +- ansible.builtin.debug: + var: cn_deletion_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_deletion_confirmation_task is success + - cn_deletion_confirmation_task is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/defaults/main.yml new file mode 100644 index 000000000..238f79fe5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/defaults/main.yml @@ -0,0 +1,13 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +name: cn-ansible-test +description: Container namespace used for testing scaleway_container_namespace_info ansible module +updated_description: Container namespace used for testing scaleway_container_namespace_info ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/tasks/main.yml new file mode 100644 index 000000000..17ac07e81 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_namespace_info/tasks/main.yml @@ -0,0 +1,41 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 container_namespace + community.general.scaleway_container_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + +- name: Get container namespace info + community.general.scaleway_container_namespace_info: + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + register: cn_info_task + +- ansible.builtin.debug: + var: cn_info_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cn_info_task is success + - cn_info_task is not changed + +- name: Delete container namespace + community.general.scaleway_container_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/defaults/main.yml new file mode 100644 index 000000000..73b31423f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/defaults/main.yml @@ -0,0 +1,9 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +name: cr_ansible_test +description: Container registry used for testing scaleway_container_registry ansible module +updated_description: Container registry used for testing scaleway_container_registry ansible module (Updated description) 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 new file mode 100644 index 000000000..91cea20f3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml @@ -0,0 +1,178 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 container registry (Check) + check_mode: true + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: cr_creation_check_task + +- ansible.builtin.debug: + var: cr_creation_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_creation_check_task is success + - cr_creation_check_task is changed + +- name: Create container_registry + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: cr_creation_task + +- ansible.builtin.debug: + var: cr_creation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_creation_task is success + - cr_creation_task is changed + - cr_creation_task.container_registry.status == "ready" + +- name: Create container registry (Confirmation) + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: cr_creation_confirmation_task + +- ansible.builtin.debug: + var: cr_creation_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_creation_confirmation_task is success + - cr_creation_confirmation_task is not changed + - cr_creation_confirmation_task.container_registry.status == "ready" + +- name: Update container registry (Check) + check_mode: true + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + register: cr_update_check_task + +- ansible.builtin.debug: + var: cr_update_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_update_check_task is success + - cr_update_check_task is changed + +- name: Update container registry + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + register: cr_update_task + +- ansible.builtin.debug: + var: cr_update_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_update_task is success + - cr_update_task is changed + - cr_update_task.container_registry.status == "ready" + +- name: Update container registry (Confirmation) + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + register: cr_update_confirmation_task + +- ansible.builtin.debug: + var: cr_update_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_update_confirmation_task is success + - cr_update_confirmation_task is not changed + - cr_update_confirmation_task.container_registry.status == "ready" + +- name: Delete container registry (Check) + check_mode: true + community.general.scaleway_container_registry: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: cr_deletion_check_task + +- ansible.builtin.debug: + var: cr_deletion_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_deletion_check_task is success + - cr_deletion_check_task is changed + +- name: Delete container registry + community.general.scaleway_container_registry: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: cr_deletion_task + +- ansible.builtin.debug: + var: cr_deletion_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_deletion_task is success + - cr_deletion_task is changed + +- name: Delete container regitry (Confirmation) + community.general.scaleway_container_registry: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: cr_deletion_confirmation_task + +- ansible.builtin.debug: + var: cr_deletion_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_deletion_confirmation_task is success + - cr_deletion_confirmation_task is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/defaults/main.yml new file mode 100644 index 000000000..8c53a31e7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/defaults/main.yml @@ -0,0 +1,9 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +name: cr_ansible_test +description: Container registry used for testing scaleway_container_registry_info ansible module +updated_description: Container registry used for testing scaleway_container_registry_info ansible module (Updated description) diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/tasks/main.yml new file mode 100644 index 000000000..7de4d245a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry_info/tasks/main.yml @@ -0,0 +1,41 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 container_registry + community.general.scaleway_container_registry: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + +- name: Get container registry info + community.general.scaleway_container_registry_info: + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + register: cr_info_task + +- ansible.builtin.debug: + var: cr_info_task + +- name: Check module call result + ansible.builtin.assert: + that: + - cr_info_task is success + - cr_info_task is not changed + +- name: Delete container registry + community.general.scaleway_container_registry: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/defaults/main.yml new file mode 100644 index 000000000..1a5306ecf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/defaults/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 + +scaleway_name: scaleway_database_backup_test +scaleway_region: fr-par +scaleway_database_name: scaleway_database_test +scaleway_instance_id: diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/tasks/main.yml new file mode 100644 index 000000000..fdef03fb3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_database_backup/tasks/main.yml @@ -0,0 +1,238 @@ +--- +#################################################################### +# 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: Create a backup (Check) + check_mode: true + scaleway_database_backup: + name: '{{ scaleway_name }}' + state: present + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + + register: backup_creation_check_task + +- debug: var=backup_creation_check_task + +- assert: + that: + - backup_creation_check_task is success + - backup_creation_check_task is changed + +- name: Create a backup + scaleway_database_backup: + name: '{{ scaleway_name }}' + state: present + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + wait: true + + register: backup_creation_task + +- debug: var=backup_creation_task + +- assert: + that: + - backup_creation_task is success + - backup_creation_task is changed + +- name: Create a backup (Confirmation) + scaleway_database_backup: + name: '{{ scaleway_name }}' + state: present + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + id: '{{ backup_creation_task.metadata.id }}' + + register: backup_creation_confirmation_task + +- debug: var=backup_creation_confirmation_task + +- assert: + that: + - backup_creation_confirmation_task is success + - backup_creation_confirmation_task is not changed + +- name: Patch backup name (Check) + check_mode: true + scaleway_database_backup: + name: '{{ scaleway_name }}-changed' + state: present + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + id: '{{ backup_creation_task.metadata.id }}' + register: backup_patching_check_task + +- debug: var=backup_patching_check_task + +- assert: + that: + - backup_patching_check_task is success + - backup_patching_check_task is changed + +- name: Patch backup name + scaleway_database_backup: + name: '{{ scaleway_name }}-changed' + state: present + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + id: '{{ backup_creation_task.metadata.id }}' + register: backup_patching_task + +- debug: var=backup_patching_task + +- assert: + that: + - backup_patching_task is success + - backup_patching_task is changed + +- name: Patch backup name (Confirmation) + scaleway_database_backup: + name: '{{ scaleway_name }}-changed' + state: present + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + id: '{{ backup_creation_task.metadata.id }}' + register: backup_patching_confirmation_task + +- debug: var=backup_patching_confirmation_task + +- assert: + that: + - backup_patching_confirmation_task is success + - backup_patching_confirmation_task is not changed + +- name: Export backup (Check) + check_mode: true + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: exported + region: '{{ scaleway_region }}' + register: backup_export_check_task + +- debug: var=backup_export_check_task + +- assert: + that: + - backup_export_check_task is success + - backup_export_check_task is changed + +- name: Export backup + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: exported + region: '{{ scaleway_region }}' + wait: true + register: backup_export_task + +- debug: var=backup_export_task + +- assert: + that: + - backup_export_task is success + - backup_export_task is changed + - backup_export_task.metadata.download_url != "" + +- name: Export backup (Confirmation) + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: exported + region: '{{ scaleway_region }}' + register: backup_export_confirmation_task + +- debug: var=backup_export_confirmation_task + +- assert: + that: + - backup_export_confirmation_task is success + - backup_export_confirmation_task is not changed + - backup_export_confirmation_task.metadata.download_url != "" + +- name: Restore backup (Check) + check_mode: true + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: restored + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + register: backup_restore_check_task + +- debug: var=backup_restore_check_task + +- assert: + that: + - backup_restore_check_task is success + - backup_restore_check_task is changed + +- name: Restore backup + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: restored + region: '{{ scaleway_region }}' + database_name: '{{ scaleway_database_name }}' + instance_id: '{{ scaleway_instance_id }}' + wait: true + register: backup_restore_task + +- debug: var=backup_restore_task + +- assert: + that: + - backup_restore_task is success + - backup_restore_task is changed + +- name: Delete backup (Check) + check_mode: true + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: absent + region: '{{ scaleway_region }}' + register: backup_delete_check_task + +- debug: var=backup_delete_check_task + +- assert: + that: + - backup_delete_check_task is success + - backup_delete_check_task is changed + +- name: Delete backup + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: absent + region: '{{ scaleway_region }}' + register: backup_delete_task + +- debug: var=backup_delete_task + +- assert: + that: + - backup_delete_task is success + - backup_delete_task is changed + +- name: Delete backup (Confirmation) + scaleway_database_backup: + id: '{{ backup_creation_task.metadata.id }}' + state: absent + region: '{{ scaleway_region }}' + register: backup_delete_confirmation_task + +- debug: var=backup_delete_confirmation_task + +- assert: + that: + - backup_delete_confirmation_task is success + - backup_delete_confirmation_task is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_function/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function/defaults/main.yml new file mode 100644 index 000000000..df02a4665 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function/defaults/main.yml @@ -0,0 +1,17 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +function_namespace_name: fn-ansible-test +name: fn-ansible-test +description: Function used for testing scaleway_function_infoansible module +updated_description: Function used for testing scaleway_function_info ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value +updated_secret_environment_variables: + MY_SECRET_VAR: my_other_secret_value +runtime: python310 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function/tasks/main.yml new file mode 100644 index 000000000..d4552d0b3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function/tasks/main.yml @@ -0,0 +1,284 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 function_namespace + community.general.scaleway_function_namespace: + state: present + name: '{{ function_namespace_name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: integration_function_namespace + +- name: Create a function (Check) + check_mode: true + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_creation_check_task + +- ansible.builtin.debug: + var: fn_creation_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_creation_check_task is success + - fn_creation_check_task is changed + +- name: Create function + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_creation_task + +- ansible.builtin.debug: + var: fn_creation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_creation_task is success + - fn_creation_task is changed + - fn_creation_task.function.status in ["created", "ready"] + +- name: Create function (Confirmation) + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_creation_confirmation_task + +- ansible.builtin.debug: + var: fn_creation_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_creation_confirmation_task is success + - fn_creation_confirmation_task is not changed + - fn_creation_confirmation_task.function.status in ["created", "ready"] + +- name: Update function (Check) + check_mode: true + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_update_check_task + +- ansible.builtin.debug: + var: fn_update_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_check_task is success + - fn_update_check_task is changed + +- name: Update function + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_update_task + +- ansible.builtin.debug: + var: fn_update_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_task is success + - fn_update_task is changed + - fn_update_task.function.status in ["created", "ready"] + +- name: Update function (Confirmation) + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_update_confirmation_task + +- ansible.builtin.debug: + var: fn_update_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_confirmation_task is success + - fn_update_confirmation_task is not changed + - fn_update_confirmation_task.function.status in ["created", "ready"] + +- name: Update function secret variables (Check) + check_mode: true + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: fn_update_secret_check_task + +- ansible.builtin.debug: + var: fn_update_secret_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_secret_check_task is success + - fn_update_secret_check_task is changed + +- name: Update function secret variables + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: fn_update_secret_task + +- ansible.builtin.debug: + var: fn_update_secret_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_secret_task is success + - fn_update_secret_task is changed + - fn_update_secret_task.function.status in ["created", "ready"] + - "'hashed_value' in fn_update_secret_task.function.secret_environment_variables[0]" + +- name: Update function secret variables (Confirmation) + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: fn_update_secret_confirmation_task + +- ansible.builtin.debug: + var: fn_update_secret_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_secret_confirmation_task is success + - fn_update_secret_confirmation_task is not changed + - fn_update_secret_confirmation_task.function.status in ["created", "ready"] + - "'hashed_value' in fn_update_secret_confirmation_task.function.secret_environment_variables[0]" + +- name: Delete function (Check) + check_mode: true + community.general.scaleway_function: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + register: fn_deletion_check_task + +- ansible.builtin.debug: + var: fn_deletion_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_deletion_check_task is success + - fn_deletion_check_task is changed + +- name: Delete function + community.general.scaleway_function: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + register: fn_deletion_task + +- ansible.builtin.debug: + var: fn_deletion_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_deletion_task is success + - fn_deletion_task is changed + +- name: Delete function (Confirmation) + community.general.scaleway_function: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + register: fn_deletion_confirmation_task + +- ansible.builtin.debug: + var: fn_deletion_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_deletion_confirmation_task is success + - fn_deletion_confirmation_task is not changed + +- name: Delete function namespace + community.general.scaleway_function_namespace: + state: absent + name: '{{ function_namespace_name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/defaults/main.yml new file mode 100644 index 000000000..71d2fe780 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/defaults/main.yml @@ -0,0 +1,15 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +function_namespace_name: fn-ansible-test +name: fn-ansible-test +description: Container used for testing scaleway_function_info ansible module +updated_description: Container used for testing scaleway_function_info ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value +runtime: python310 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/tasks/main.yml new file mode 100644 index 000000000..17b07f5f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_info/tasks/main.yml @@ -0,0 +1,62 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 function_namespace + community.general.scaleway_function_namespace: + state: present + name: '{{ function_namespace_name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + register: integration_function_namespace + +- name: Create function + community.general.scaleway_function: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + +- name: Get function info + community.general.scaleway_function_info: + name: '{{ name }}' + region: '{{ scaleway_region }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + register: fn_info_task + +- ansible.builtin.debug: + var: fn_info_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_info_task is success + - fn_info_task is not changed + +- name: Delete function + community.general.scaleway_function: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + namespace_id: '{{ integration_function_namespace.function_namespace.id }}' + runtime: '{{ runtime }}' + +- name: Delete function namespace + community.general.scaleway_function_namespace: + state: absent + name: '{{ function_namespace_name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/defaults/main.yml new file mode 100644 index 000000000..399d0ea1a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/defaults/main.yml @@ -0,0 +1,15 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +name: fn-ansible-test +description: Function namespace used for testing scaleway_function_namespace ansible module +updated_description: Function namespace used for testing scaleway_function_namespace ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value +updated_secret_environment_variables: + MY_SECRET_VAR: my_other_secret_value diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/tasks/main.yml new file mode 100644 index 000000000..50af4449d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace/tasks/main.yml @@ -0,0 +1,260 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 function namespace (Check) + check_mode: true + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_creation_check_task + +- ansible.builtin.debug: + var: fn_creation_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_creation_check_task is success + - fn_creation_check_task is changed + +- name: Create function_namespace + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_creation_task + +- ansible.builtin.debug: + var: fn_creation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_creation_task is success + - fn_creation_task is changed + - fn_creation_task.function_namespace.status == "ready" + - "'hashed_value' in fn_creation_task.function_namespace.secret_environment_variables[0]" + +- name: Create function namespace (Confirmation) + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_creation_confirmation_task + +- ansible.builtin.debug: + var: fn_creation_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_creation_confirmation_task is success + - fn_creation_confirmation_task is not changed + - fn_creation_confirmation_task.function_namespace.status == "ready" + - "'hashed_value' in fn_creation_task.function_namespace.secret_environment_variables[0]" + +- name: Update function namespace (Check) + check_mode: true + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_update_check_task + +- ansible.builtin.debug: + var: fn_update_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_check_task is success + - fn_update_check_task is changed + +- name: Update function namespace + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_update_task + +- ansible.builtin.debug: + var: fn_update_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_task is success + - fn_update_task is changed + - fn_update_task.function_namespace.status == "ready" + - "'hashed_value' in fn_creation_task.function_namespace.secret_environment_variables[0]" + +- name: Update function namespace (Confirmation) + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ secret_environment_variables }}' + register: fn_update_confirmation_task + +- ansible.builtin.debug: + var: fn_update_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_confirmation_task is success + - fn_update_confirmation_task is not changed + - fn_update_confirmation_task.function_namespace.status == "ready" + - "'hashed_value' in fn_creation_task.function_namespace.secret_environment_variables[0]" + +- name: Update function namespace secret variables (Check) + check_mode: true + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: fn_update_secret_check_task + +- ansible.builtin.debug: + var: fn_update_secret_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_secret_check_task is success + - fn_update_secret_check_task is changed + +- name: Update function namespace secret variables + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: fn_update_secret_task + +- ansible.builtin.debug: + var: fn_update_secret_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_secret_task is success + - fn_update_secret_task is changed + - fn_update_secret_task.function_namespace.status == "ready" + - "'hashed_value' in fn_update_secret_task.function_namespace.secret_environment_variables[0]" + +- name: Update function namespace secret variables (Confirmation) + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ updated_description }}' + environment_variables: '{{ environment_variables }}' + secret_environment_variables: '{{ updated_secret_environment_variables }}' + register: fn_update_secret_confirmation_task + +- ansible.builtin.debug: + var: fn_update_secret_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_update_secret_confirmation_task is success + - fn_update_secret_confirmation_task is not changed + - fn_update_secret_confirmation_task.function_namespace.status == "ready" + - "'hashed_value' in fn_update_secret_confirmation_task.function_namespace.secret_environment_variables[0]" + +- name: Delete function namespace (Check) + check_mode: true + community.general.scaleway_function_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: fn_deletion_check_task + +- ansible.builtin.debug: + var: fn_deletion_check_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_deletion_check_task is success + - fn_deletion_check_task is changed + +- name: Delete function namespace + community.general.scaleway_function_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: fn_deletion_task + +- ansible.builtin.debug: + var: fn_deletion_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_deletion_task is success + - fn_deletion_task is changed + - "'hashed_value' in fn_creation_task.function_namespace.secret_environment_variables[0]" + +- name: Delete function namespace (Confirmation) + community.general.scaleway_function_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' + register: fn_deletion_confirmation_task + +- ansible.builtin.debug: + var: fn_deletion_confirmation_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_deletion_confirmation_task is success + - fn_deletion_confirmation_task is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/aliases new file mode 100644 index 000000000..a5ac5181f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/defaults/main.yml new file mode 100644 index 000000000..0b05eaac0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/defaults/main.yml @@ -0,0 +1,13 @@ +--- +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 + +scaleway_region: fr-par +name: fn-ansible-test +description: Function namespace used for testing scaleway_function_namespace_info ansible module +updated_description: Function namespace used for testing scaleway_function_namespace_info ansible module (Updated description) +environment_variables: + MY_VAR: my_value +secret_environment_variables: + MY_SECRET_VAR: my_secret_value diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/tasks/main.yml new file mode 100644 index 000000000..793cd0923 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_function_namespace_info/tasks/main.yml @@ -0,0 +1,43 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2022, Guillaume MARTINEZ +# 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 function_namespace + community.general.scaleway_function_namespace: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + description: '{{ description }}' + secret_environment_variables: '{{ secret_environment_variables }}' + +- name: Get function namespace info + community.general.scaleway_function_namespace_info: + name: '{{ name }}' + region: '{{ scaleway_region }}' + project_id: '{{ scw_project }}' + register: fn_info_task + +- ansible.builtin.debug: + var: fn_info_task + +- name: Check module call result + ansible.builtin.assert: + that: + - fn_info_task is success + - fn_info_task is not changed + - "'hashed_value' in fn_info_task.function_namespace.secret_environment_variables[0]" + +- name: Delete function namespace + community.general.scaleway_function_namespace: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + project_id: '{{ scw_project }}' diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..2cdf34fdd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: Get image informations and register it in a variable + scaleway_image_info: + region: par1 + register: images + +- name: Display images variable + debug: + var: images + +- name: Ensure retrieval of images info is success + assert: + that: + - images is success + +- name: Get image informations from ams1 and register it in a variable + scaleway_image_info: + region: ams1 + register: images_ams1 + +- name: Display images variable from ams1 + debug: + var: images_ams1 + +- name: Ensure retrieval of images info is success + assert: + that: + - images_ams1 is success diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_ip/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_ip/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_ip/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_ip/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_ip/defaults/main.yml new file mode 100644 index 000000000..00ec26ff6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_ip/defaults/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 + +scaleway_organization: '{{ scw_org }}' +scaleway_region: ams1 +scaleway_image_id: 89ee4018-f8c3-4dc4-a6b5-bca14f985ebe +scaleway_commerial_type: START1-S +scaleway_server_name: scaleway_ip_test_server +scaleway_reverse_name: scaleway.com diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_ip/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_ip/tasks/main.yml new file mode 100644 index 000000000..5a396ba4f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_ip/tasks/main.yml @@ -0,0 +1,449 @@ +--- +#################################################################### +# 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: Create IP (Check) + check_mode: true + scaleway_ip: + state: present + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: ip_creation_check_task + +- debug: var=ip_creation_check_task + +- name: ip_creation_check_task is success + assert: + that: + - ip_creation_check_task is success + +- name: ip_creation_check_task is changed + assert: + that: + - ip_creation_check_task is changed + +- name: Create IP + scaleway_ip: + state: present + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: ip_creation_task + +- debug: var=ip_creation_task + +- name: ip_creation_task is success + assert: + that: + - ip_creation_task is success + +- name: ip_creation_task is changed + assert: + that: + - ip_creation_task is changed + +- name: ip_creation_task.scaleway_ip.server is none + assert: + that: + - '{{ ip_creation_task.scaleway_ip.server is none }}' + +- name: Create IP (Confirmation) + scaleway_ip: + id: '{{ ip_creation_task.scaleway_ip.id }}' + state: present + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: ip_creation_confirmation_task + +- debug: var=ip_creation_confirmation_task + +- name: ip_creation_confirmation_task is success + assert: + that: + - ip_creation_confirmation_task is success + +- name: ip_creation_confirmation_task is not changed + assert: + that: + - ip_creation_confirmation_task is not changed + +- name: ip_creation_confirmation_task.scaleway_ip.server is none + assert: + that: + - '{{ ip_creation_task.scaleway_ip.server is none }}' + +- name: Assign reverse to server (Check) + check_mode: true + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_reverse_assignation_check_task + +- debug: var=ip_reverse_assignation_check_task + +- name: ip_reverse_assignation_check_task is success + assert: + that: + - ip_reverse_assignation_check_task is success + +- name: ip_reverse_assignation_check_task is success + assert: + that: + - ip_reverse_assignation_check_task is success + +- name: Assign reverse to an IP + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_reverse_assignation_task + +- debug: var=ip_reverse_assignation_task + +- name: ip_reverse_assignation_task is success + assert: + that: + - ip_reverse_assignation_task is success + +- name: ip_reverse_assignation_task is changed + assert: + that: + - ip_reverse_assignation_task is changed + +- name: Assign reverse to an IP (Confirmation) + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_reverse_assignation_confirmation_task + +- debug: var=ip_reverse_assignation_confirmation_task + +- name: ip_reverse_assignation_confirmation_task is success + assert: + that: + - ip_reverse_assignation_confirmation_task is success + +- name: ip_reverse_assignation_confirmation_task is not changed + assert: + that: + - ip_reverse_assignation_confirmation_task is not changed + +- name: Create a server + scaleway_compute: + state: present + name: '{{ scaleway_server_name }}' + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + dynamic_ip_required: false + wait: true + + register: server_creation_task + +- debug: var=server_creation_task + +- name: server_creation_task is success + assert: + that: + - server_creation_task is success + +- name: Assign IP to server (Check) + check_mode: true + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + server: '{{ server_creation_task.msg.id }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_assignation_check_task + +- debug: var=ip_assignation_check_task + +- name: ip_assignation_check_task is success + assert: + that: + - ip_assignation_check_task is success + +- name: ip_assignation_check_task is success + assert: + that: + - ip_assignation_check_task is success + +- name: Assign IP to server + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + server: '{{ server_creation_task.msg.id }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_assignation_task + +- debug: var=ip_assignation_task + +- name: ip_assignation_task is success + assert: + that: + - ip_assignation_task is success + +- name: ip_assignation_task is changed + assert: + that: + - ip_assignation_task is changed + +- name: Assign IP to server (Confirmation) + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + server: '{{ server_creation_task.msg.id }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_assignation_confirmation_task + +- debug: var=ip_assignation_confirmation_task + +- name: ip_assignation_confirmation_task is success + assert: + that: + - ip_assignation_confirmation_task is success + +- name: ip_assignation_confirmation_task is not changed + assert: + that: + - ip_assignation_confirmation_task is not changed + +- name: Unassign IP to server (Check) + check_mode: true + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_unassignation_check_task + +- debug: var=ip_unassignation_check_task + +- name: ip_unassignation_check_task is success + assert: + that: + - ip_unassignation_check_task is success + +- name: ip_unassignation_check_task is changed + assert: + that: + - ip_unassignation_check_task is changed + +- name: Unassign IP to server + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_unassignation_task + +- debug: var=ip_unassignation_task + +- name: ip_unassignation_task is success + assert: + that: + - ip_unassignation_task is success + +- name: ip_unassignation_task is changed + assert: + that: + - ip_unassignation_task is changed + +- name: ip_unassignation_task.scaleway_ip.server is none + assert: + that: + - '{{ ip_unassignation_task.scaleway_ip.server is none }}' + +- name: Unassign IP to server (Confirmation) + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + reverse: '{{ scaleway_reverse_name }}' + register: ip_unassignation_confirmation_task + +- debug: var=ip_unassignation_confirmation_task + +- name: ip_unassignation_confirmation_task is success + assert: + that: + - ip_unassignation_confirmation_task is success + +- name: ip_unassignation_confirmation_task is not changed + assert: + that: + - ip_unassignation_confirmation_task is not changed + +- name: ip_unassignation_confirmation_task.scaleway_ip.server is none + assert: + that: + - '{{ ip_unassignation_task.scaleway_ip.server is none }}' + +- name: Unassign reverse to IP (Check) + check_mode: true + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: ip_reverse_unassignation_check_task + +- debug: var=ip_reverse_unassignation_check_task + +- name: ip_reverse_unassignation_check_task is success + assert: + that: + - ip_reverse_unassignation_check_task is success + +- name: ip_reverse_unassignation_check_task is changed + assert: + that: + - ip_reverse_unassignation_check_task is changed + +- name: Unassign reverse to an IP + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: ip_reverse_unassignation_task + +- debug: var=ip_reverse_unassignation_task + +- name: ip_reverse_unassignation_task is success + assert: + that: + - ip_reverse_unassignation_task is success + +- name: ip_reverse_unassignation_task is changed + assert: + that: + - ip_reverse_unassignation_task is changed + +- name: ip_reverse_unassignation_task.scaleway_ip.reverse is none + assert: + that: + - '{{ ip_reverse_unassignation_task.scaleway_ip.reverse is none }}' + +- name: Unassign reverse to an IP (Confirmation) + scaleway_ip: + state: present + id: '{{ ip_creation_task.scaleway_ip.id }}' + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: ip_reverse_unassignation_confirmation_task + +- debug: var=ip_reverse_unassignation_confirmation_task + +- name: ip_reverse_unassignation_confirmation_task is success + assert: + that: + - ip_reverse_unassignation_confirmation_task is success + +- name: ip_reverse_unassignation_confirmation_task is not changed + assert: + that: + - ip_reverse_unassignation_confirmation_task is not changed + +- name: ip_reverse_unassignation_confirmation_task.scaleway_ip.server is none + assert: + that: + - '{{ ip_reverse_unassignation_confirmation_task.scaleway_ip.reverse is none }}' + +- name: Destroy a server + scaleway_compute: + name: '{{ scaleway_server_name }}' + state: absent + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + register: server_destroy_task + +- debug: var=server_destroy_task + +- name: server_destroy_task is success + assert: + that: + - server_destroy_task is success + +- name: server_destroy_task is changed + assert: + that: + - server_destroy_task is changed + +- name: Delete IP (Check) + check_mode: true + scaleway_ip: + state: absent + region: '{{ scaleway_region }}' + id: '{{ ip_creation_task.scaleway_ip.id }}' + register: ip_deletion_check_task + +- name: ip_deletion_check_task is success + assert: + that: + - ip_deletion_check_task is success + +- name: ip_deletion_check_task is changed + assert: + that: + - ip_deletion_check_task is changed + +- name: Delete IP + scaleway_ip: + state: absent + region: '{{ scaleway_region }}' + id: '{{ ip_creation_task.scaleway_ip.id }}' + register: ip_deletion_task + +- name: ip_deletion_task is success + assert: + that: + - ip_deletion_task is success + +- name: ip_deletion_task is changed + assert: + that: + - ip_deletion_task is changed + +- name: Delete IP (Confirmation) + scaleway_ip: + state: absent + region: '{{ scaleway_region }}' + id: '{{ ip_creation_task.scaleway_ip.id }}' + register: ip_deletion_confirmation_task + +- name: ip_deletion_confirmation_task is success + assert: + that: + - ip_deletion_confirmation_task is success + +- name: ip_deletion_confirmation_task is not changed + assert: + that: + - ip_deletion_confirmation_task is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..b560b5658 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: Get ip informations and register it in a variable + scaleway_ip_info: + region: par1 + register: ips + +- name: Display ips variable + debug: + var: ips + +- name: Ensure retrieval of ips info is success + assert: + that: + - ips is success + +- name: Get ip informations and register it in a variable + scaleway_ip_info: + region: ams1 + register: ips_ams1 + +- name: Display ips variable + debug: + var: ips_ams1 + +- name: Ensure retrieval of ips info is success + assert: + that: + - ips_ams1 is success diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_lb/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_lb/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_lb/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_lb/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_lb/defaults/main.yml new file mode 100644 index 000000000..c0d37800a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_lb/defaults/main.yml @@ -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 + +scaleway_region: fr-par +name: lb_ansible_test +description: Load-balancer used for testing scaleway_lb ansible module +updated_description: Load-balancer used for testing scaleway_lb ansible module (Updated description) +tags: + - first_tag + - second_tag diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_lb/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_lb/tasks/main.yml new file mode 100644 index 000000000..2567abd07 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_lb/tasks/main.yml @@ -0,0 +1,224 @@ +--- +#################################################################### +# 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: Create a load-balancer (Check) + check_mode: true + scaleway_lb: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + organization_id: '{{ scw_org }}' + description: '{{ description }}' + tags: '{{ tags }}' + register: lb_creation_check_task + +- debug: var=lb_creation_check_task + +- name: lb_creation_check_task is success + assert: + that: + - lb_creation_check_task is success + +- name: lb_creation_check_task is changed + assert: + that: + - lb_creation_check_task is changed + +- name: Create load-balancer + scaleway_lb: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + organization_id: '{{ scw_org }}' + description: '{{ description }}' + tags: '{{ tags }}' + wait: true + register: lb_creation_task + +- debug: var=lb_creation_task + +- name: lb_creation_task is success + assert: + that: + - lb_creation_task is success + +- name: lb_creation_task is changed + assert: + that: + - lb_creation_task is changed + +- name: Assert that the load-balancer is in a valid state + assert: + that: + - lb_creation_task.scaleway_lb.status == "ready" + +- name: Create load-balancer (Confirmation) + scaleway_lb: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + organization_id: '{{ scw_org }}' + tags: '{{ tags }}' + description: '{{ description }}' + register: lb_creation_confirmation_task + +- debug: var=lb_creation_confirmation_task + +- name: lb_creation_confirmation_task is success + assert: + that: + - lb_creation_confirmation_task is success + +- name: lb_creation_confirmation_task is not changed + assert: + that: + - lb_creation_confirmation_task is not changed + +- name: Assert that the load-balancer is in a valid state + assert: + that: + - lb_creation_confirmation_task.scaleway_lb.status == "ready" + +- name: Update load-balancer (Check) + check_mode: true + scaleway_lb: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + organization_id: '{{ scw_org }}' + tags: '{{ tags }}' + description: '{{ updated_description }}' + register: lb_update_check_task + +- debug: var=lb_update_check_task + +- name: lb_update_check_task is success + assert: + that: + - lb_update_check_task is success + +- name: lb_update_check_task is changed + assert: + that: + - lb_update_check_task is changed + +- name: Update load-balancer + scaleway_lb: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + tags: '{{ tags }}' + organization_id: '{{ scw_org }}' + description: '{{ updated_description }}' + wait: true + register: lb_update_task + +- debug: var=lb_update_task + +- name: lb_update_task is success + assert: + that: + - lb_update_task is success + +- name: lb_update_task is changed + assert: + that: + - lb_update_task is changed + +- name: Assert that the load-balancer is in a valid state + assert: + that: + - lb_update_task.scaleway_lb.status == "ready" + +- name: Update load-balancer (Confirmation) + scaleway_lb: + state: present + name: '{{ name }}' + region: '{{ scaleway_region }}' + tags: '{{ tags }}' + organization_id: '{{ scw_org }}' + description: '{{ updated_description }}' + register: lb_update_confirmation_task + +- debug: var=lb_update_confirmation_task + +- name: lb_update_confirmation_task is success + assert: + that: + - lb_update_confirmation_task is success + +- name: lb_update_confirmation_task is not changed + assert: + that: + - lb_update_confirmation_task is not changed + +- name: Assert that the load-balancer is in a valid state + assert: + that: + - lb_update_confirmation_task.scaleway_lb.status == "ready" + +- name: Delete load-balancer (Check) + check_mode: true + scaleway_lb: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + organization_id: '{{ scw_org }}' + register: lb_deletion_check_task + +- name: lb_deletion_check_task is success + assert: + that: + - lb_deletion_check_task is success + +- name: lb_deletion_check_task is changed + assert: + that: + - lb_deletion_check_task is changed + +- name: Delete load-balancer + scaleway_lb: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + organization_id: '{{ scw_org }}' + wait: true + register: lb_deletion_task + +- name: lb_deletion_task is success + assert: + that: + - lb_deletion_task is success + +- name: lb_deletion_task is changed + assert: + that: + - lb_deletion_task is changed + +- name: Delete load-balancer (Confirmation) + scaleway_lb: + state: absent + name: '{{ name }}' + region: '{{ scaleway_region }}' + description: '{{ description }}' + organization_id: '{{ scw_org }}' + register: lb_deletion_confirmation_task + +- name: lb_deletion_confirmation_task is success + assert: + that: + - lb_deletion_confirmation_task is success + +- name: lb_deletion_confirmation_task is not changed + assert: + that: + - lb_deletion_confirmation_task is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..7326ca226 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml @@ -0,0 +1,22 @@ +--- +#################################################################### +# 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: Get organization informations and register it in a variable + scaleway_organization_info: + register: organizations + +- name: Display organizations variable + debug: + var: organizations + +- name: Ensure retrieval of organizations info is success + assert: + that: + - organizations is success diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/defaults/main.yml new file mode 100644 index 000000000..ffada8080 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/defaults/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 + +scaleway_organization: '{{ scw_org }}' +scaleway_region: ams1 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/tasks/main.yml new file mode 100644 index 000000000..cab972ae5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group/tasks/main.yml @@ -0,0 +1,139 @@ +--- +#################################################################### +# 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: Create security group check + check_mode: true + scaleway_security_group: + state: present + region: '{{ scaleway_region }}' + name: security_group + description: 'my security group description' + organization: '{{ scaleway_organization }}' + stateful: false + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group_creation + +- debug: var=security_group_creation + +- name: Ensure security groups check facts is success + assert: + that: + - security_group_creation is success + - security_group_creation is changed + +- block: + - name: Create security group + scaleway_security_group: + state: present + region: '{{ scaleway_region }}' + name: security_group + description: 'my security group description' + organization: '{{ scaleway_organization }}' + stateful: false + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group_creation + + - debug: var=security_group_creation + + - name: Ensure security groups facts is success + assert: + that: + - security_group_creation is success + - security_group_creation is changed + + - name: Create security group duplicate + scaleway_security_group: + state: present + region: '{{ scaleway_region }}' + name: security_group + description: 'my security group description' + organization: '{{ scaleway_organization }}' + stateful: false + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group_creation + + - debug: var=security_group_creation + + - name: Ensure security groups duplicate facts is success + assert: + that: + - security_group_creation is success + - security_group_creation is not changed + + - name: Delete security group check + check_mode: true + scaleway_security_group: + state: absent + region: '{{ scaleway_region }}' + name: security_group + description: 'my security group description' + organization: '{{ scaleway_organization }}' + stateful: false + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group_deletion + + - debug: var=security_group_deletion + + - name: Ensure security groups delete check facts is success + assert: + that: + - security_group_deletion is success + - security_group_deletion is changed + + always: + - name: Delete security group + scaleway_security_group: + state: absent + region: '{{ scaleway_region }}' + name: security_group + description: 'my security group description' + organization: '{{ scaleway_organization }}' + stateful: false + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group_deletion + + - debug: var=security_group_deletion + + - name: Ensure security groups delete facts is success + assert: + that: + - security_group_deletion is success + - security_group_deletion is changed + +- name: Delete security group duplicate + scaleway_security_group: + state: absent + region: '{{ scaleway_region }}' + name: security_group + description: 'my security group description' + organization: '{{ scaleway_organization }}' + stateful: false + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group_deletion + +- debug: var=security_group_deletion + +- name: Ensure security groups delete duplicate facts is success + assert: + that: + - security_group_deletion is success + - security_group_deletion is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..8029a1e9a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: Get security group informations and register it in a variable + scaleway_security_group_info: + region: par1 + register: security_groups + +- name: Display security_groups variable + debug: + var: security_groups + +- name: Ensure retrieval of security groups info is success + assert: + that: + - security_groups is success + +- name: Get security group informations and register it in a variable (AMS1) + scaleway_security_group_info: + region: ams1 + register: ams1_security_groups + +- name: Display security_groups variable (AMS1) + debug: + var: ams1_security_groups + +- name: Ensure retrieval of security groups info is success (AMS1) + assert: + that: + - ams1_security_groups is success diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/defaults/main.yml new file mode 100644 index 000000000..965ccf594 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/defaults/main.yml @@ -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 + +scaleway_organization: '{{ scw_org }}' +scaleway_region: par1 +protocol: "TCP" +port: 80 +ip_range: "0.0.0.0/0" +direction: "inbound" +action: "accept" diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/tasks/main.yml new file mode 100644 index 000000000..383942195 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_rule/tasks/main.yml @@ -0,0 +1,252 @@ +--- +#################################################################### +# 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: Create a scaleway security_group + scaleway_security_group: + state: present + region: '{{ scaleway_region }}' + name: test_compute + description: test_compute + organization: '{{ scaleway_organization }}' + stateful: true + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false + register: security_group + +- debug: var=security_group + +- name: Create security_group_rule check + check_mode: true + scaleway_security_group_rule: + state: present + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: '{{ port }}' + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_creation_task + +- debug: var=security_group_rule_creation_task + +- assert: + that: + - security_group_rule_creation_task is success + - security_group_rule_creation_task is changed + +- block: + - name: Create security_group_rule check + scaleway_security_group_rule: + state: present + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: '{{ port }}' + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_creation_task + + - debug: var=security_group_rule_creation_task + + - assert: + that: + - security_group_rule_creation_task is success + - security_group_rule_creation_task is changed + + - name: Create security_group_rule duplicate + scaleway_security_group_rule: + state: present + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: '{{ port }}' + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_creation_task + + - debug: var=security_group_rule_creation_task + + - assert: + that: + - security_group_rule_creation_task is success + - security_group_rule_creation_task is not changed + + - name: Delete security_group_rule check + check_mode: true + scaleway_security_group_rule: + state: absent + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: '{{ port }}' + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_deletion_task + + - debug: var=security_group_rule_deletion_task + + - assert: + that: + - security_group_rule_deletion_task is success + - security_group_rule_deletion_task is changed + + always: + - name: Delete security_group_rule check + scaleway_security_group_rule: + state: absent + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: '{{ port }}' + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_deletion_task + + - debug: var=security_group_rule_deletion_task + + - assert: + that: + - security_group_rule_deletion_task is success + - security_group_rule_deletion_task is changed + +- name: Delete security_group_rule check + scaleway_security_group_rule: + state: absent + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: '{{ port }}' + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_deletion_task + +- debug: var=security_group_rule_deletion_task + +- assert: + that: + - security_group_rule_deletion_task is success + - security_group_rule_deletion_task is not changed + +- block: + - name: Create security_group_rule with null check + scaleway_security_group_rule: + state: present + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: null + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_creation_task + + - debug: var=security_group_rule_creation_task + + - assert: + that: + - security_group_rule_creation_task is success + - security_group_rule_creation_task is changed + + - name: Create security_group_rule with null duplicate + scaleway_security_group_rule: + state: present + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: null + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_creation_task + + - debug: var=security_group_rule_creation_task + + - assert: + that: + - security_group_rule_creation_task is success + - security_group_rule_creation_task is not changed + + - name: Delete security_group_rule with null check + check_mode: true + scaleway_security_group_rule: + state: absent + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: null + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_deletion_task + + - debug: var=security_group_rule_deletion_task + + - assert: + that: + - security_group_rule_deletion_task is success + - security_group_rule_deletion_task is changed + + always: + - name: Delete security_group_rule with null check + scaleway_security_group_rule: + state: absent + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: null + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_deletion_task + + - debug: var=security_group_rule_deletion_task + + - assert: + that: + - security_group_rule_deletion_task is success + - security_group_rule_deletion_task is changed + +- name: Delete security_group_rule with null check + scaleway_security_group_rule: + state: absent + region: '{{ scaleway_region }}' + protocol: '{{ protocol }}' + port: null + ip_range: '{{ ip_range }}' + direction: '{{ direction }}' + action: '{{ action }}' + security_group: '{{ security_group.scaleway_security_group.id }}' + register: security_group_rule_deletion_task + +- debug: var=security_group_rule_deletion_task + +- assert: + that: + - security_group_rule_deletion_task is success + - security_group_rule_deletion_task is not changed + +- name: Delete scaleway security_group + scaleway_security_group: + state: absent + region: '{{ scaleway_region }}' + name: test_compute + description: test_compute + organization: '{{ scaleway_organization }}' + stateful: true + inbound_default_policy: accept + outbound_default_policy: accept + organization_default: false diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..7274e8a85 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: Get server informations and register it in a variable + scaleway_server_info: + region: par1 + register: servers + +- name: Display servers variable + debug: + var: servers + +- name: Ensure retrieval of servers info is success + assert: + that: + - servers is success + +- name: Get server informations and register it in a variable + scaleway_server_info: + region: ams1 + register: ams1_servers + +- name: Display servers variable + debug: + var: ams1_servers + +- name: Ensure retrieval of servers info is success + assert: + that: + - ams1_servers is success diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..44f15d515 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: Get snapshot informations and register it in a variable + scaleway_snapshot_info: + region: par1 + register: snapshots + +- name: Display snapshots variable + debug: + var: snapshots + +- name: Ensure retrieval of snapshots info is success + assert: + that: + - snapshots is success + +- name: Get snapshot informations and register it in a variable (AMS1) + scaleway_snapshot_info: + region: ams1 + register: ams1_snapshots + +- name: Display snapshots variable (AMS1) + debug: + var: ams1_snapshots + +- name: Ensure retrieval of snapshots info is success (AMS1) + assert: + that: + - ams1_snapshots is success diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/tasks/main.yml new file mode 100644 index 000000000..588745abd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_sshkey/tasks/main.yml @@ -0,0 +1,49 @@ +--- +#################################################################### +# 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 + +- scaleway_sshkey: + ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf29yyommeGyKSIgSmX0ISVXP+3x6RUY4JDGLoAMFh2efkfDaRVdsvkvnFuUywgP2RewrjTyLE8w0NpCBHVS5Fm1BAn3yvxOUtTMxTbsQcw6HQ8swJ02+1tewJYjHPwc4GrBqiDo3Nmlq354Us0zBOJg/bBzuEnVD5eJ3GO3gKaCSUYTVrYwO0U4eJE0D9OJeUP9J48kl4ULbCub976+mTHdBvlzRw0Tzfl2kxgdDwlks0l2NefY/uiTdz2oMt092bAY3wZHxjto/DXoChxvaf5s2k8Zb+J7CjimUYnzPlH+zA9F6ROjP5AUu6ZWPd0jOIBl1nDWWb2j/qfNLYM43l sieben@sieben-macbook.local" + state: present + check_mode: true + +- scaleway_sshkey: + ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf29yyommeGyKSIgSmX0ISVXP+3x6RUY4JDGLoAMFh2efkfDaRVdsvkvnFuUywgP2RewrjTyLE8w0NpCBHVS5Fm1BAn3yvxOUtTMxTbsQcw6HQ8swJ02+1tewJYjHPwc4GrBqiDo3Nmlq354Us0zBOJg/bBzuEnVD5eJ3GO3gKaCSUYTVrYwO0U4eJE0D9OJeUP9J48kl4ULbCub976+mTHdBvlzRw0Tzfl2kxgdDwlks0l2NefY/uiTdz2oMt092bAY3wZHxjto/DXoChxvaf5s2k8Zb+J7CjimUYnzPlH+zA9F6ROjP5AUu6ZWPd0jOIBl1nDWWb2j/qfNLYM43l sieben@sieben-macbook.local" + state: present + register: result1 + +- scaleway_sshkey: + ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf29yyommeGyKSIgSmX0ISVXP+3x6RUY4JDGLoAMFh2efkfDaRVdsvkvnFuUywgP2RewrjTyLE8w0NpCBHVS5Fm1BAn3yvxOUtTMxTbsQcw6HQ8swJ02+1tewJYjHPwc4GrBqiDo3Nmlq354Us0zBOJg/bBzuEnVD5eJ3GO3gKaCSUYTVrYwO0U4eJE0D9OJeUP9J48kl4ULbCub976+mTHdBvlzRw0Tzfl2kxgdDwlks0l2NefY/uiTdz2oMt092bAY3wZHxjto/DXoChxvaf5s2k8Zb+J7CjimUYnzPlH+zA9F6ROjP5AUu6ZWPd0jOIBl1nDWWb2j/qfNLYM43l sieben@sieben-macbook.local" + state: present + register: result2 + +- assert: + that: + - result1 is success and result1 is changed + - result2 is success and result2 is not changed + +- scaleway_sshkey: + ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf29yyommeGyKSIgSmX0ISVXP+3x6RUY4JDGLoAMFh2efkfDaRVdsvkvnFuUywgP2RewrjTyLE8w0NpCBHVS5Fm1BAn3yvxOUtTMxTbsQcw6HQ8swJ02+1tewJYjHPwc4GrBqiDo3Nmlq354Us0zBOJg/bBzuEnVD5eJ3GO3gKaCSUYTVrYwO0U4eJE0D9OJeUP9J48kl4ULbCub976+mTHdBvlzRw0Tzfl2kxgdDwlks0l2NefY/uiTdz2oMt092bAY3wZHxjto/DXoChxvaf5s2k8Zb+J7CjimUYnzPlH+zA9F6ROjP5AUu6ZWPd0jOIBl1nDWWb2j/qfNLYM43l sieben@sieben-macbook.local" + state: absent + check_mode: true + +- scaleway_sshkey: + ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf29yyommeGyKSIgSmX0ISVXP+3x6RUY4JDGLoAMFh2efkfDaRVdsvkvnFuUywgP2RewrjTyLE8w0NpCBHVS5Fm1BAn3yvxOUtTMxTbsQcw6HQ8swJ02+1tewJYjHPwc4GrBqiDo3Nmlq354Us0zBOJg/bBzuEnVD5eJ3GO3gKaCSUYTVrYwO0U4eJE0D9OJeUP9J48kl4ULbCub976+mTHdBvlzRw0Tzfl2kxgdDwlks0l2NefY/uiTdz2oMt092bAY3wZHxjto/DXoChxvaf5s2k8Zb+J7CjimUYnzPlH+zA9F6ROjP5AUu6ZWPd0jOIBl1nDWWb2j/qfNLYM43l sieben@sieben-macbook.local" + state: absent + register: result1 + +- scaleway_sshkey: + ssh_pub_key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDf29yyommeGyKSIgSmX0ISVXP+3x6RUY4JDGLoAMFh2efkfDaRVdsvkvnFuUywgP2RewrjTyLE8w0NpCBHVS5Fm1BAn3yvxOUtTMxTbsQcw6HQ8swJ02+1tewJYjHPwc4GrBqiDo3Nmlq354Us0zBOJg/bBzuEnVD5eJ3GO3gKaCSUYTVrYwO0U4eJE0D9OJeUP9J48kl4ULbCub976+mTHdBvlzRw0Tzfl2kxgdDwlks0l2NefY/uiTdz2oMt092bAY3wZHxjto/DXoChxvaf5s2k8Zb+J7CjimUYnzPlH+zA9F6ROjP5AUu6ZWPd0jOIBl1nDWWb2j/qfNLYM43l sieben@sieben-macbook.local" + state: absent + register: result2 + +- assert: + that: + - result1 is success and result1 is changed + - result2 is success and result2 is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/defaults/main.yml new file mode 100644 index 000000000..7c53696b6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/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 + +cloud_init_script: ''' +#cloud-config + +# final_message +# default: cloud-init boot finished at $TIMESTAMP. Up $UPTIME seconds +# this message is written by cloud-final when the system is finished +# its first boot +final_message: "The system is finally up, after $UPTIME seconds" +''' +scaleway_image_id: 89ee4018-f8c3-4dc4-a6b5-bca14f985ebe +scaleway_organization: '{{ scw_org }}' +scaleway_region: ams1 +scaleway_commerial_type: START1-S diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/tasks/main.yml new file mode 100644 index 000000000..ce4284127 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_user_data/tasks/main.yml @@ -0,0 +1,87 @@ +--- +#################################################################### +# 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: Create a server + scaleway_compute: + name: foobar + state: present + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + region: '{{ scaleway_region }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + + register: server_creation_task + +- debug: var=server_creation_task + +- set_fact: + server_id: "{{ server_creation_task.msg.id }}" + +- debug: var=server_id + +- name: Patch user_data cloud-init configuration (Check) + check_mode: true + scaleway_user_data: + region: '{{ scaleway_region }}' + server_id: "{{ server_id }}" + user_data: + cloud-init: "{{ cloud_init_script }}" + register: user_data_check_task + +- debug: var=user_data_check_task + +- assert: + that: + - user_data_check_task is success + - user_data_check_task is changed + +- name: Patch user_data cloud-init configuration + scaleway_user_data: + region: '{{ scaleway_region }}' + server_id: "{{ server_id }}" + user_data: + cloud-init: "{{ cloud_init_script }}" + register: user_data_task + +- debug: var=user_data_task + +- assert: + that: + - user_data_task is success + - user_data_task is changed + +- name: Patch user_data cloud-init configuration (Confirmation) + scaleway_user_data: + region: '{{ scaleway_region }}' + server_id: "{{ server_id }}" + user_data: + cloud-init: "{{ cloud_init_script }}" + register: user_data_confirmation_task + +- debug: var=user_data_confirmation_task + +- assert: + that: + - user_data_confirmation_task is success + - user_data_confirmation_task is not changed + +- name: Destroy it + scaleway_compute: + name: foobar + state: absent + region: '{{ scaleway_region }}' + image: '{{ scaleway_image_id }}' + organization: '{{ scaleway_organization }}' + commercial_type: '{{ scaleway_commerial_type }}' + wait: true + register: server_destroy_task + +- debug: var=server_destroy_task diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_volume/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_volume/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_volume/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 + +cloud/scaleway +unsupported diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_volume/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_volume/defaults/main.yml new file mode 100644 index 000000000..ffada8080 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_volume/defaults/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 + +scaleway_organization: '{{ scw_org }}' +scaleway_region: ams1 diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_volume/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_volume/tasks/main.yml new file mode 100644 index 000000000..2828a8502 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_volume/tasks/main.yml @@ -0,0 +1,51 @@ +--- +#################################################################### +# 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: Make sure volume is not there before tests + scaleway_volume: + name: ansible-test-volume + state: absent + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: server_creation_check_task + +- assert: + that: + - server_creation_check_task is success + +- name: Create volume + scaleway_volume: + name: ansible-test-volume + state: present + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + "size": 10000000000 + volume_type: l_ssd + register: server_creation_check_task + +- debug: var=server_creation_check_task + +- assert: + that: + - server_creation_check_task is success + - server_creation_check_task is changed + +- name: Make sure volume is deleted + scaleway_volume: + name: ansible-test-volume + state: absent + region: '{{ scaleway_region }}' + organization: '{{ scaleway_organization }}' + register: server_creation_check_task + +- assert: + that: + - server_creation_check_task is success + - server_creation_check_task is changed diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/aliases b/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/aliases new file mode 100644 index 000000000..b2267f631 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/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 + +cloud/scaleway +unsupported 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 new file mode 100644 index 000000000..45995a54c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: Get volume informations and register it in a variable + scaleway_volume_info: + region: par1 + register: volumes + +- name: Display volumes variable + debug: + var: volumes + +- name: Ensure retrieval of volumes info is success + assert: + that: + - volumes is success + +- name: Get volume informations and register it in a variable (AMS1) + scaleway_volume_info: + region: ams1 + register: ams1_volumes + +- name: Display volumes variable + debug: + var: ams1_volumes + +- name: Ensure retrieval of volumes info is success + assert: + that: + - ams1_volumes is success diff --git a/ansible_collections/community/general/tests/integration/targets/sefcontext/aliases b/ansible_collections/community/general/tests/integration/targets/sefcontext/aliases new file mode 100644 index 000000000..d318128f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sefcontext/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 +needs/root +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/sefcontext/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/sefcontext/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sefcontext/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/sefcontext/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/main.yml new file mode 100644 index 000000000..04143d1cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/main.yml @@ -0,0 +1,21 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2016, Dag Wieers +# 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 + +# FIXME: Unfortunately ansible_selinux could be a boolean or a dictionary ! +- debug: + msg: SELinux is disabled + when: ansible_selinux is defined and ansible_selinux == False + +- debug: + msg: SELinux is {{ ansible_selinux.status }} + when: ansible_selinux is defined and ansible_selinux != False + +- include_tasks: sefcontext.yml + when: ansible_selinux is defined and ansible_selinux != False and ansible_selinux.status == 'enabled' diff --git a/ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/sefcontext.yml b/ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/sefcontext.yml new file mode 100644 index 000000000..258f1ace9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sefcontext/tasks/sefcontext.yml @@ -0,0 +1,233 @@ +--- +# Copyright (c) 2016, Dag Wieers +# 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 requirements for RHEL + package: + name: policycoreutils-python + when: + - ansible_distribution == 'RedHat' + - ansible_distribution_major_version|int < 8 + +- name: install requirements for rhel8 beta + package: + name: python3-policycoreutils + when: + - ansible_distribution == 'RedHat' + - ansible_distribution_major_version|int >= 8 + +- name: Ensure we start with a clean state + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: httpd_sys_content_t + state: absent + +- name: Ensure we start with a clean state + sefcontext: + path: /tmp/foo + state: absent + +- name: Set SELinux file context of foo/bar + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: httpd_sys_content_t + state: present + reload: false + register: first + +- assert: + that: + - first is changed + - first.setype == 'httpd_sys_content_t' + +- name: Set SELinux file context of foo/bar (again) + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: httpd_sys_content_t + state: present + reload: false + register: second + +- assert: + that: + - second is not changed + - second.setype == 'httpd_sys_content_t' + +- name: Change SELinux file context of foo/bar + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: unlabeled_t + state: present + reload: false + register: third + +- assert: + that: + - third is changed + - third.setype == 'unlabeled_t' + +- name: Change SELinux file context of foo/bar (again) + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: unlabeled_t + state: present + reload: false + register: fourth + +- assert: + that: + - fourth is not changed + - fourth.setype == 'unlabeled_t' + +- name: Delete SELinux file context of foo/bar + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: httpd_sys_content_t + state: absent + reload: false + register: fifth + +- assert: + that: + - fifth is changed + - fifth.setype == 'httpd_sys_content_t' + +- name: Delete SELinux file context of foo/bar (again) + sefcontext: + path: '/tmp/foo/bar(/.*)?' + setype: unlabeled_t + state: absent + reload: false + register: sixth + +- assert: + that: + - sixth is not changed + - sixth.setype == 'unlabeled_t' + +- name: Set SELinux file context path substitution of foo + sefcontext: + path: /tmp/foo + substitute: /home + state: present + reload: false + register: subst_first + +- assert: + that: + - subst_first is changed + - subst_first.substitute == '/home' + +- name: Set SELinux file context path substitution of foo (again) + sefcontext: + path: /tmp/foo + substitute: /home + state: present + reload: false + register: subst_second + +- assert: + that: + - subst_second is not changed + - subst_second.substitute == '/home' + +- name: Change SELinux file context path substitution of foo + sefcontext: + path: /tmp/foo + substitute: /boot + state: present + reload: false + register: subst_third + +- assert: + that: + - subst_third is changed + - subst_third.substitute == '/boot' + +- name: Change SELinux file context path substitution of foo (again) + sefcontext: + path: /tmp/foo + substitute: /boot + state: present + reload: false + register: subst_fourth + +- assert: + that: + - subst_fourth is not changed + - subst_fourth.substitute == '/boot' + +- name: Try to delete non-existing SELinux file context path substitution of foo + sefcontext: + path: /tmp/foo + substitute: /dev + state: absent + reload: false + register: subst_fifth + +- assert: + that: + - subst_fifth is not changed + - subst_fifth.substitute == '/dev' + +- name: Delete SELinux file context path substitution of foo + sefcontext: + path: /tmp/foo + substitute: /boot + state: absent + reload: false + register: subst_sixth + +- assert: + that: + - subst_sixth is changed + - subst_sixth.substitute == '/boot' + +- name: Delete SELinux file context path substitution of foo (again) + sefcontext: + path: /tmp/foo + substitute: /boot + state: absent + reload: false + register: subst_seventh + +- assert: + that: + - subst_seventh is not changed + - subst_seventh.substitute == '/boot' + +- name: Set SELinux file context path substitution of foo + sefcontext: + path: /tmp/foo + substitute: /home + state: present + reload: false + register: subst_eighth + +- assert: + that: + - subst_eighth is changed + - subst_eighth.substitute == '/home' + +- name: Delete SELinux file context path substitution of foo + sefcontext: + path: /tmp/foo + state: absent + reload: false + register: subst_ninth + +- assert: + that: + - subst_ninth is changed + +- name: Delete SELinux file context path substitution of foo (again) + sefcontext: + path: /tmp/foo + state: absent + reload: false + register: subst_tenth + +- assert: + that: + - subst_tenth is not changed diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_client/aliases b/ansible_collections/community/general/tests/integration/targets/sensu_client/aliases new file mode 100644 index 000000000..bca9905ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_client/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/1 +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_client/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/sensu_client/tasks/main.yml new file mode 100644 index 000000000..61e49cda0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_client/tasks/main.yml @@ -0,0 +1,179 @@ +--- +#################################################################### +# 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: Creating a client if the directory doesn't exist should work + sensu_client: + subscriptions: + - default + +- name: Set variable for client file + set_fact: + client_file: "/etc/sensu/conf.d/client.json" + +- name: Insert invalid JSON in the client file + lineinfile: + state: "present" + create: "yes" + path: "{{ client_file }}" + line: "{'foo' = bar}" + +- name: Configure Sensu client with an existing invalid file + sensu_client: + name: "client" + state: "present" + subscriptions: + - default + register: client + +- name: Retrieve configuration file + slurp: + src: "{{ client_file }}" + register: client_config + +- name: Assert that client data was set successfully and properly + assert: + that: + - "client is successful" + - "client is changed" + - "client['config']['name'] == 'client'" + - "'default' in client['config']['subscriptions']" + - "client['file'] == client_file" + +- name: Assert that the client configuration file is actually configured properly + vars: + config: "{{ client_config.content | b64decode | from_json }}" + assert: + that: + - "config['client']['keepalives'] == true" + - "config['client']['name'] == 'client'" + - "config['client']['safe_mode'] == false" + - "'default' in config['client']['subscriptions']" + +- name: Delete Sensu client configuration + sensu_client: + state: "absent" + register: client_delete + +- name: Delete Sensu client configuration (again) + sensu_client: + state: "absent" + register: client_delete_twice + +- name: Retrieve configuration file stat + stat: + path: "{{ client_file }}" + register: client_stat + +- name: Assert that client deletion was successful + assert: + that: + - "client_delete is successful" + - "client_delete is changed" + - "client_delete_twice is successful" + - "client_delete_twice is not changed" + - "client_stat.stat.exists == false" + +- name: Configuring a client without subscriptions should fail + sensu_client: + name: "failure" + register: failure + ignore_errors: true + +- name: Assert failure to create client + assert: + that: + - failure is failed + - "'the following are missing: subscriptions' in failure['msg']" + +- name: Configure a new client from scratch with custom parameters + sensu_client: + name: "custom" + address: "host.fqdn" + subscriptions: + - "default" + - "webserver" + redact: + - "password" + socket: + bind: "127.0.0.1" + port: "3030" + keepalive: + thresholds: + warning: "180" + critical: "300" + handlers: + - "email" + custom: + - broadcast: "irc" + occurrences: "3" + register: client + +- name: Configure a new client from scratch with custom parameters (twice) + sensu_client: + name: "custom" + address: "host.fqdn" + subscriptions: + - "default" + - "webserver" + redact: + - "password" + socket: + bind: "127.0.0.1" + port: "3030" + keepalive: + thresholds: + warning: "180" + critical: "300" + handlers: + - "email" + custom: + - broadcast: "irc" + occurrences: "3" + register: client_twice + +- name: Retrieve configuration file + slurp: + src: "{{ client_file }}" + register: client_config + +- name: Assert that client data was set successfully and properly + assert: + that: + - "client is successful" + - "client is changed" + - "client_twice is successful" + - "client_twice is not changed" + - "client['config']['name'] == 'custom'" + - "client['config']['address'] == 'host.fqdn'" + - "'default' in client['config']['subscriptions']" + - "'webserver' in client['config']['subscriptions']" + - "'password' in client['config']['redact']" + - "client['config']['keepalive']['thresholds']['warning'] == '180'" + - "client['config']['keepalive']['thresholds']['critical'] == '300'" + - "'email' in client['config']['keepalive']['handlers']" + - "client['config']['keepalive']['occurrences'] == '3'" + - "client['file'] == client_file" + +- name: Assert that the client configuration file is actually configured properly + vars: + config: "{{ client_config.content | b64decode | from_json }}" + assert: + that: + - "config['client']['name'] == 'custom'" + - "config['client']['address'] == 'host.fqdn'" + - "config['client']['keepalives'] == true" + - "config['client']['safe_mode'] == false" + - "'default' in config['client']['subscriptions']" + - "'webserver' in config['client']['subscriptions']" + - "'password' in config['client']['redact']" + - "config['client']['keepalive']['thresholds']['warning'] == '180'" + - "config['client']['keepalive']['thresholds']['critical'] == '300'" + - "'email' in config['client']['keepalive']['handlers']" + - "config['client']['keepalive']['occurrences'] == '3'" diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/aliases b/ansible_collections/community/general/tests/integration/targets/sensu_handler/aliases new file mode 100644 index 000000000..bca9905ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/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/1 +needs/root diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/main.yml new file mode 100644 index 000000000..ec73a14c4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/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: Creating a handler if the directory doesn't exist should work + sensu_handler: + name: "handler" + type: "pipe" + command: "/bin/bash" + state: "present" + +- name: Insert junk JSON in a handlers file + lineinfile: + state: "present" + create: "yes" + path: "/etc/sensu/conf.d/handlers/handler.json" + line: "{'foo' = bar}" + +- name: Configure a handler with an existing invalid file + sensu_handler: + name: "handler" + type: "pipe" + command: "/bin/bash" + state: "present" + register: handler + +- name: Configure a handler (again) + sensu_handler: + name: "handler" + type: "pipe" + command: "/bin/bash" + state: "present" + register: handler_twice + +- name: Retrieve configuration file + slurp: + src: "{{ handler['file'] }}" + register: handler_config + +- name: Assert that handler data was set successfully and properly + assert: + that: + - "handler is successful" + - "handler is changed" + - "handler_twice is successful" + - "handler_twice is not changed" + - "handler['name'] == 'handler'" + - "handler['file'] == '/etc/sensu/conf.d/handlers/handler.json'" + - "handler['config']['type'] == 'pipe'" + - "handler['config']['command'] == '/bin/bash'" + - "handler['config']['timeout'] == 10" + - "handler['config']['handle_flapping'] == false" + - "handler['config']['handle_silenced'] == false" + +- name: Assert that the handler configuration file is actually configured properly + vars: + config: "{{ handler_config.content | b64decode | from_json }}" + assert: + that: + - "'handler' in config['handlers']" + - "config['handlers']['handler']['type'] == 'pipe'" + - "config['handlers']['handler']['command'] == '/bin/bash'" + - "config['handlers']['handler']['timeout'] == 10" + - "config['handlers']['handler']['handle_flapping'] == false" + - "config['handlers']['handler']['handle_silenced'] == false" + +- name: Delete Sensu handler configuration + sensu_handler: + name: "handler" + state: "absent" + register: handler_delete + +- name: Delete Sensu handler configuration (again) + sensu_handler: + name: "handler" + state: "absent" + register: handler_delete_twice + +- name: Retrieve configuration file stat + stat: + path: "{{ handler['file'] }}" + register: handler_stat + +- name: Assert that handler deletion was successful + assert: + that: + - "handler_delete is successful" + - "handler_delete is changed" + - "handler_delete_twice is successful" + - "handler_delete_twice is not changed" + - "handler_stat.stat.exists == false" + +- name: Configuring a handler without a name should fail + sensu_handler: + type: "pipe" + command: "/bin/bash" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler without a name fails + assert: + that: + - failure is failed + - "'required arguments: name' in failure['msg']" + +- name: Configuring a handler without a type should fail + sensu_handler: + name: "pipe" + command: "/bin/bash" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler without a type fails + assert: + that: + - failure is failed + - "'the following are missing: type' in failure['msg']" + +- include_tasks: pipe.yml +- include_tasks: tcp.yml +- include_tasks: udp.yml +- include_tasks: set.yml +- include_tasks: transport.yml diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/pipe.yml b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/pipe.yml new file mode 100644 index 000000000..46fe24080 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/pipe.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 + +# Note: Pipe handlers are also tested and used as part of basic main.yml coverage +- name: Configuring a handler with missing pipe parameters should fail + sensu_handler: + name: "pipe" + type: "pipe" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler with missing pipe parameters fails + assert: + that: + - failure is failed + - "'the following are missing: command' in failure['msg']" + +- name: Configure a handler with pipe parameters + sensu_handler: + name: "pipe" + type: "pipe" + command: "/bin/bash" + register: handler diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/set.yml b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/set.yml new file mode 100644 index 000000000..e9a86057c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/set.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 + +- name: Configuring a handler with missing set parameters should fail + sensu_handler: + name: "set" + type: "set" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler with missing set parameters fails + assert: + that: + - failure is failed + - "'the following are missing: handlers' in failure['msg']" + +- name: Configure a set handler + sensu_handler: + name: "set" + type: "set" + handlers: + - anotherhandler + register: handler + +- name: Retrieve configuration file + slurp: + src: "{{ handler['file'] }}" + register: handler_config + +- name: Validate set handler return data + assert: + that: + - "handler is successful" + - "handler is changed" + - "handler['name'] == 'set'" + - "handler['file'] == '/etc/sensu/conf.d/handlers/set.json'" + - "handler['config']['type'] == 'set'" + - "'anotherhandler' in handler['config']['handlers']" + - "handler['config']['handle_flapping'] == false" + - "handler['config']['handle_silenced'] == false" + +- name: Assert that the handler configuration file is actually configured properly + vars: + config: "{{ handler_config.content | b64decode | from_json }}" + assert: + that: + - "'set' in config['handlers']" + - "config['handlers']['set']['type'] == 'set'" + - "'anotherhandler' in config['handlers']['set']['handlers']" + - "config['handlers']['set']['handle_flapping'] == false" + - "config['handlers']['set']['handle_silenced'] == false" diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/tcp.yml b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/tcp.yml new file mode 100644 index 000000000..a5db1d397 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/tcp.yml @@ -0,0 +1,56 @@ +--- +# 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: Configuring a handler with missing tcp parameters should fail + sensu_handler: + name: "tcp" + type: "tcp" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler with missing tcp parameters fails + assert: + that: + - failure is failed + - "'the following are missing: socket' in failure['msg']" + +- name: Configure a tcp handler + sensu_handler: + name: "tcp" + type: "tcp" + socket: + host: 127.0.0.1 + port: 8000 + register: handler + +- name: Retrieve configuration file + slurp: + src: "{{ handler['file'] }}" + register: handler_config + +- name: Validate tcp handler return data + assert: + that: + - "handler is successful" + - "handler is changed" + - "handler['name'] == 'tcp'" + - "handler['file'] == '/etc/sensu/conf.d/handlers/tcp.json'" + - "handler['config']['type'] == 'tcp'" + - "handler['config']['socket']['host'] == '127.0.0.1'" + - "handler['config']['socket']['port'] == 8000" + - "handler['config']['handle_flapping'] == false" + - "handler['config']['handle_silenced'] == false" + +- name: Assert that the handler configuration file is actually configured properly + vars: + config: "{{ handler_config.content | b64decode | from_json }}" + assert: + that: + - "'tcp' in config['handlers']" + - "config['handlers']['tcp']['type'] == 'tcp'" + - "config['handlers']['tcp']['socket']['host'] == '127.0.0.1'" + - "config['handlers']['tcp']['socket']['port'] == 8000" + - "config['handlers']['tcp']['handle_flapping'] == false" + - "config['handlers']['tcp']['handle_silenced'] == false" diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/transport.yml b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/transport.yml new file mode 100644 index 000000000..fa2563fa9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/transport.yml @@ -0,0 +1,56 @@ +--- +# 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: Configuring a handler with missing transport parameters should fail + sensu_handler: + name: "transport" + type: "transport" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler with missing transport parameters fails + assert: + that: + - failure is failed + - "'the following are missing: pipe' in failure['msg']" + +- name: Configure a transport handler + sensu_handler: + name: "transport" + type: "transport" + pipe: + type: "topic" + name: "transport_handler" + register: handler + +- name: Retrieve configuration file + slurp: + src: "{{ handler['file'] }}" + register: handler_config + +- name: Validate transport handler return data + assert: + that: + - "handler is successful" + - "handler is changed" + - "handler['name'] == 'transport'" + - "handler['file'] == '/etc/sensu/conf.d/handlers/transport.json'" + - "handler['config']['type'] == 'transport'" + - "handler['config']['pipe']['type'] == 'topic'" + - "handler['config']['pipe']['name'] == 'transport_handler'" + - "handler['config']['handle_flapping'] == false" + - "handler['config']['handle_silenced'] == false" + +- name: Assert that the handler configuration file is actually configured properly + vars: + config: "{{ handler_config.content | b64decode | from_json }}" + assert: + that: + - "'transport' in config['handlers']" + - "config['handlers']['transport']['type'] == 'transport'" + - "config['handlers']['transport']['pipe']['type'] == 'topic'" + - "config['handlers']['transport']['pipe']['name'] == 'transport_handler'" + - "config['handlers']['transport']['handle_flapping'] == false" + - "config['handlers']['transport']['handle_silenced'] == false" diff --git a/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/udp.yml b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/udp.yml new file mode 100644 index 000000000..60e88bb98 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sensu_handler/tasks/udp.yml @@ -0,0 +1,56 @@ +--- +# 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: Configuring a handler with missing udp parameters should fail + sensu_handler: + name: "udp" + type: "udp" + register: failure + ignore_errors: true + +- name: Assert that configuring a handler with missing udp parameters fails + assert: + that: + - failure is failed + - "'the following are missing: socket' in failure['msg']" + +- name: Configure a udp handler + sensu_handler: + name: "udp" + type: "udp" + socket: + host: 127.0.0.1 + port: 8000 + register: handler + +- name: Retrieve configuration file + slurp: + src: "{{ handler['file'] }}" + register: handler_config + +- name: Validate udp handler return data + assert: + that: + - "handler is successful" + - "handler is changed" + - "handler['name'] == 'udp'" + - "handler['file'] == '/etc/sensu/conf.d/handlers/udp.json'" + - "handler['config']['type'] == 'udp'" + - "handler['config']['socket']['host'] == '127.0.0.1'" + - "handler['config']['socket']['port'] == 8000" + - "handler['config']['handle_flapping'] == false" + - "handler['config']['handle_silenced'] == false" + +- name: Assert that the handler configuration file is actually configured properly + vars: + config: "{{ handler_config.content | b64decode | from_json }}" + assert: + that: + - "'udp' in config['handlers']" + - "config['handlers']['udp']['type'] == 'udp'" + - "config['handlers']['udp']['socket']['host'] == '127.0.0.1'" + - "config['handlers']['udp']['socket']['port'] == 8000" + - "config['handlers']['udp']['handle_flapping'] == false" + - "config['handlers']['udp']['handle_silenced'] == false" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/defaults/main.yml new file mode 100644 index 000000000..aa7de77fe --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/defaults/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 + +remote_dir: "{{ lookup('env', 'OUTPUT_DIR') }}" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/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/setup_cron/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/tasks/main.yml new file mode 100644 index 000000000..cca7071a3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/tasks/main.yml @@ -0,0 +1,75 @@ +--- +#################################################################### +# 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 + +- when: + - not (ansible_os_family == 'Alpine' and ansible_distribution_version is version('3.15', '<')) # TODO + block: + - name: Include distribution specific variables + include_vars: '{{ lookup(''first_found'', search) }}' + vars: + search: + files: + - '{{ ansible_distribution | lower }}.yml' + - '{{ ansible_os_family | lower }}.yml' + - '{{ ansible_system | lower }}.yml' + - default.yml + paths: + - vars + - name: install cron package + package: + name: '{{ cron_pkg }}' + when: cron_pkg | default(false, true) + register: cron_package_installed + until: cron_package_installed is success + - when: faketime_pkg | default(false, true) + block: + - name: install cron and faketime packages + package: + name: '{{ faketime_pkg }}' + register: faketime_package_installed + until: faketime_package_installed is success + - name: Find libfaketime path + shell: '{{ list_pkg_files }} {{ faketime_pkg }} | grep -F libfaketime.so.1' + register: libfaketime_path + - when: ansible_service_mgr == 'systemd' + block: + - name: create directory for cron drop-in file + file: + path: /etc/systemd/system/{{ cron_service }}.service.d + state: directory + owner: root + group: root + mode: '0755' + - name: Use faketime with cron service + copy: + content: '[Service] + + Environment=LD_PRELOAD={{ libfaketime_path.stdout_lines[0].strip() }} + + Environment="FAKETIME=+0y x10" + + Environment=RANDOM_DELAY=0' + dest: /etc/systemd/system/{{ cron_service }}.service.d/faketime.conf + owner: root + group: root + mode: '0644' + - when: ansible_system == 'FreeBSD' + name: Use faketime with cron service + copy: + content: cron_env='LD_PRELOAD={{ libfaketime_path.stdout_lines[0].strip() }} FAKETIME="+0y x10"' + dest: /etc/rc.conf.d/cron + owner: root + group: wheel + mode: '0644' + - name: enable cron service + service: + daemon-reload: '{{ (ansible_service_mgr == ''systemd'') | ternary(true, omit) }}' + name: '{{ cron_service }}' + state: restarted diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/alpine.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/alpine.yml new file mode 100644 index 000000000..7c28829c6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/alpine.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 + +cron_pkg: cronie +cron_service: cronie +list_pkg_files: apk info -L diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/archlinux.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/archlinux.yml new file mode 100644 index 000000000..c714683ad --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/archlinux.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 + +cron_pkg: cronie +cron_service: cronie +list_pkg_files: pacman -Ql diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/debian.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/debian.yml new file mode 100644 index 000000000..1eefe007c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/debian.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 + +cron_pkg: cron +cron_service: cron +list_pkg_files: dpkg -L diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/default.yml new file mode 100644 index 000000000..f55df21f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/default.yml @@ -0,0 +1,4 @@ +--- +# 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_cron/vars/fedora.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/fedora.yml new file mode 100644 index 000000000..0d17db6ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/fedora.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 + +cron_pkg: cronie +cron_service: crond +list_pkg_files: rpm -ql diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/freebsd.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/freebsd.yml new file mode 100644 index 000000000..284871016 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/freebsd.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 + +cron_pkg: +cron_service: cron +list_pkg_files: pkg info --list-files diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/redhat.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/redhat.yml new file mode 100644 index 000000000..2998f7b84 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/redhat.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 + +cron_pkg: cronie +cron_service: crond +faketime_pkg: +list_pkg_files: rpm -ql diff --git a/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/suse.yml b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/suse.yml new file mode 100644 index 000000000..77e7e09e3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_cron/vars/suse.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 + +cron_pkg: cron +cron_service: cron +list_pkg_files: rpm -ql diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/README.md b/ansible_collections/community/general/tests/integration/targets/setup_docker/README.md new file mode 100644 index 000000000..f2f26b7cc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/README.md @@ -0,0 +1,73 @@ + + +Setup Docker +============ + +This role provides a mechanism to install docker automatically within the context of an integration test. + +For the time being (Apr 2023) it has been tested in Fedora 37 and Ubuntu Jammy. + +This role was largely based on the `setup_snap` one written by @felixfontein. + + +Quickstart +---------- + +Add the file `meta/main.yml` to your integration test target it it does not yet contain one, and add (or update) the `dependencies` block with `setup_docker`, as in: + +```yaml +dependencies: + - setup_docker +``` + +In your integration test target, add to the beginning of the `tasks/main.yml` something like (example from `mssql_script`): + +```yaml +- name: Start container + community.docker.docker_container: + name: mssql-test + image: "mcr.microsoft.com/mssql/server:2019-latest" + env: + ACCEPT_EULA: "Y" + SA_PASSWORD: "{{ mssql_login_password }}" + MSSQL_PID: Developer + ports: + - "{{ mssql_port }}:1433" + detach: true + auto_remove: true + memory: 2200M +``` + +That's it! Your integration test will be using a docker container to support the test. + + +What it does +------------ + +The role will install `docker` on the test target, allowing the test to run a container to support its execution. + +The installation of the package sends a notification to an Ansible handler that will remove `docker` from the system after the integration test target is done. + +This role assumes that developers will use the collection `community.docker` to manage the containers used in the test. To support that assumption, this role will install the `requests` package in the Python runtime environment used, usually a *virtualenv* used for the test. That package is **not removed** from that environment after the test. + +The most common use case is to use `community.docker.docker_container` to start a container, as in the example above. It is likely that `community.docker.docker_compose` can be used as well, although this has **not been tested** yet. + + +Recommendations +--------------- + +* Don't forget to publish the service ports when starting the container +* Take into consideration that the services inside the container will take a while to get started. Use both/either `ansible.builtin.wait_for` to check for the availability of the network port and/or `retries` on the first task effectively using those services +* As a precautionary measure, start using the role in a test that is marked either `disabled` or `unsupported`, and move forward from there. + + +Known Issues & Caveats +---------------------- + +* Support only Ubuntu and Fedora, having been tested in Ubuntu Jammy and Fedora 37, respectively +* Lack mechanism to choose or constraint the `docker` version to be used +* Lack option to prevent `docker` from being removed at the end of the integration test diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/aliases b/ansible_collections/community/general/tests/integration/targets/setup_docker/aliases new file mode 100644 index 000000000..0a430dff1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/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 + +needs/target/setup_epel diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/defaults/main.yml new file mode 100644 index 000000000..55dc6fdb2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/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 + +distro_lookup_names: + - "D-{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_version }}.yml" + - "D-{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml" + - "{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml" + - "D-{{ ansible_facts.distribution }}.yml" + - "{{ ansible_facts.os_family }}.yml" + - "default.yml" + +has_docker: false 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 new file mode 100644 index 000000000..283496714 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/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 + +- name: Remove Docker packages + package: + name: "{{ docker_packages }}" + state: absent + +- name: "D-Fedora : Remove repository" + file: + path: /etc/yum.repos.d/docker-ce.repo + state: absent + +- name: "D-Fedora : Remove dnf-plugins-core" + package: + name: dnf-plugins-core + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/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/setup_docker/tasks/D-Fedora.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/D-Fedora.yml new file mode 100644 index 000000000..80d6e869d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/D-Fedora.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 + +# dnf -y install dnf-plugins-core +# dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo +# sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin + +- name: Install dnf-plugins-core + become: true + package: + name: dnf-plugins-core + state: present + notify: "D-Fedora : Remove dnf-plugins-core" + +- name: Add docker repo + become: true + ansible.builtin.command: + cmd: dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo + notify: "D-Fedora : Remove repository" + +- name: Install docker + become: true + package: + name: "{{ item }}" + state: present + loop: "{{ docker_packages }}" + notify: Remove Docker packages + +- name: Inform that docker is installed + set_fact: + has_docker: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/default.yml new file mode 100644 index 000000000..a628e074b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/default.yml @@ -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 + +# dnf -y install dnf-plugins-core +# dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo +# sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin + +- name: Install docker + become: true + package: + name: "{{ item }}" + state: present + loop: "{{ docker_packages }}" + notify: + - Remove Docker packages + +- name: Inform that docker is installed + set_fact: + has_docker: true 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 new file mode 100644 index 000000000..4f41da31a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml @@ -0,0 +1,55 @@ +--- +#################################################################### +# 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: Print information on which we distinguish + debug: + msg: "Distribution '{{ ansible_facts.distribution }}', version '{{ ansible_facts.distribution_version }}', OS family '{{ ansible_facts.os_family }}'" + +- name: Install EPEL repository (RHEL only) + include_role: + name: setup_epel + when: + - ansible_distribution in ['RedHat', 'CentOS'] + - ansible_distribution_major_version is version('9', '<') + +- name: Distribution specific + block: + - name: Include distribution specific vars + include_vars: "{{ lookup('first_found', params) }}" + vars: + params: + files: "{{ distro_lookup_names }}" + paths: + - "{{ role_path }}/vars" + - name: Include distribution specific tasks + include_tasks: "{{ lookup('first_found', params) }}" + vars: + params: + files: "{{ distro_lookup_names }}" + paths: + - "{{ role_path }}/tasks" + +- name: Start docker service + become: true + ansible.builtin.service: + name: docker + state: started + +- name: Cheat on the docker socket permissions + become: true + ansible.builtin.file: + path: /var/run/docker.sock + mode: 0666 + +- name: Install python "requests" + ansible.builtin.pip: + name: + - requests + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Fedora.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Fedora.yml new file mode 100644 index 000000000..f03626f4a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Fedora.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 + +docker_packages: + - docker-ce + - docker-ce-cli + - containerd.io + - docker-compose-plugin diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Ubuntu.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Ubuntu.yml new file mode 100644 index 000000000..260dc1d5e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/vars/D-Ubuntu.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 + +docker_packages: + - docker.io diff --git a/ansible_collections/community/general/tests/integration/targets/setup_epel/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_epel/tasks/main.yml new file mode 100644 index 000000000..186d515f4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_epel/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) 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 EPEL + yum: + name: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/setup_epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm + disable_gpg_check: true + when: + - ansible_facts.distribution in ['RedHat', 'CentOS'] + - ansible_facts.distribution_major_version == '6' + +- name: Install EPEL + yum: + name: https://ci-files.testing.ansible.com/test/integration/targets/setup_epel/epel-release-latest-{{ ansible_distribution_major_version }}.noarch.rpm + disable_gpg_check: true + when: + - ansible_facts.distribution in ['RedHat', 'CentOS'] + - ansible_facts.distribution_major_version != '6' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/defaults/main.yml new file mode 100644 index 000000000..f185ef0c2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/defaults/main.yml @@ -0,0 +1,17 @@ +--- +# setup etcd3 for integration tests on module/lookup +# (c) 2017, Jean-Philippe Evrard +# 2020, SCC France, Eric Belhomme +# +# 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 +# # Copyright (c) 2018, Ansible Project +# +etcd3_ver: "v3.2.14" +etcd3_download_server: "https://storage.googleapis.com/etcd" +#etcd3_download_server: "https://github.com/coreos/etcd/releases/download" +etcd3_download_url: "{{ etcd3_download_server }}/{{ etcd3_ver }}/etcd-{{ etcd3_ver }}-linux-amd64.tar.gz" +etcd3_download_location: /tmp/etcd-download-test +etcd3_path: "{{ etcd3_download_location }}/etcd-{{ etcd3_ver }}-linux-amd64" + +etcd3_pip_module: etcd3>=0.12 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/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/setup_etcd3/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml new file mode 100644 index 000000000..fe6b9cd02 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml @@ -0,0 +1,104 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# setup etcd3 for integration tests on module/lookup +# Copyright 2017, Jean-Philippe Evrard +# Copyright 2020, SCC France, Eric Belhomme +# 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 etcd3 for supported distros +- block: + + - name: python 2 + set_fact: + python_suffix: "" + when: ansible_python_version is version('3', '<') + + - name: python 3 + set_fact: + python_suffix: "-py3" + when: ansible_python_version is version('3', '>=') + + - include_vars: '{{ item }}' + with_first_found: + - files: + - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml' + - '{{ ansible_distribution }}-{{ ansible_distribution_version }}{{ python_suffix }}.yml' + - '{{ ansible_os_family }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml' + - '{{ ansible_os_family }}{{ python_suffix }}.yml' + - 'default{{ python_suffix }}.yml' + - 'default.yml' + paths: '../vars' + + - name: Upgrade setuptools python2 module + pip: + name: setuptools<45 + extra_args: --upgrade + state: present + when: python_suffix == '' + + - name: Install etcd3 python modules + pip: + name: "{{ etcd3_pip_module }}" + extra_args: --only-binary grpcio + state: present + + # Check if re-installing etcd3 is required + - name: Check if etcd3ctl exists for re-use. + shell: "ETCDCTL_API=3 {{ etcd3_path }}/etcdctl --endpoints=localhost:2379 get foo" + args: + executable: /bin/bash + changed_when: false + failed_when: false + register: _testetcd3ctl + + - block: + # Installing etcd3 + - name: If can't reuse, prepare download folder + file: + path: "{{ etcd3_download_location }}" + state: directory + register: _etcddownloadexists + when: + - _testetcd3ctl.rc != 0 + + - name: Delete download folder if already exists (to start clean) + file: + path: "{{ etcd3_download_location }}" + state: absent + when: + - _etcddownloadexists is not changed + + - name: Recreate download folder if purged + file: + path: "{{ etcd3_download_location }}" + state: directory + when: + - _etcddownloadexists is not changed + + - name: Download etcd3 + unarchive: + src: "{{ etcd3_download_url }}" + dest: "{{ etcd3_download_location }}" + remote_src: true + + # Running etcd3 and kill afterwards if it wasn't running before. + - name: Run etcd3 + shell: "{{ etcd3_path }}/etcd &" + register: _etcd3run + changed_when: true + +# - name: kill etcd3 +# command: "pkill etcd" + + when: + - _testetcd3ctl.rc != 0 + + when: + - ansible_distribution | lower ~ "-" ~ ansible_distribution_major_version | lower != 'centos-6' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/RedHat-7.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/RedHat-7.yml new file mode 100644 index 000000000..6e1017fe8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/RedHat-7.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 + +etcd3_pip_module: etcd3<0.12 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse-py3.yml new file mode 100644 index 000000000..4e7c275b8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse-py3.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 + +# SuSE's python 3.6.10 comes with six 1.11.0 as distutil +# we restrict to etcd3 < 0.11 to avoid pip to try to upgrade six +etcd3_pip_module: 'etcd3<0.11' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse.yml new file mode 100644 index 000000000..4e7c275b8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/Suse.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 + +# SuSE's python 3.6.10 comes with six 1.11.0 as distutil +# we restrict to etcd3 < 0.11 to avoid pip to try to upgrade six +etcd3_pip_module: 'etcd3<0.11' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/default.yml new file mode 100644 index 000000000..f7e08fa31 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/vars/default.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 + +# default should don't touch anything diff --git a/ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/README.md b/ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/README.md new file mode 100644 index 000000000..44dfeb0c6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/README.md @@ -0,0 +1,144 @@ + + +# Create a dummy flatpak repository remote + +This document describes how to create a local flatpak dummy repo. Just like the one contained in the `files/repo.tar.gxz` archive. + + +## Create a hello world app + +Prerequisites: + + - flathub + +Prepare the environment: + +``` +flatpak install --system flathub org.freedesktop.Platform//1.6 org.freedesktop.Sdk//1.6 +``` + +Create a hello world executable: + +``` +echo $'#!/bin/sh\necho hello world' > hello.sh +``` + +To create dummy flatpaks, run this (defining a unique NUM for every flatpak to add): + +``` +export NUM=1 +flatpak build-init appdir$NUM com.dummy.App$NUM org.freedesktop.Sdk org.freedesktop.Platform 1.6; +flatpak build appdir$NUM mkdir /app/bin; +flatpak build appdir$NUM install --mode=750 hello.sh /app/bin; +flatpak build-finish --command=hello.sh appdir$NUM +``` + +## Create a repo and/or add the app to it + +Create a repo and add the file to it in one command: + +``` +flatpak build-export repo appdir$NUM stable +``` + +## Create flatpak*-files + +Put a flatpakref file under the repo folder (`repo/com.dummy.App1.flatpakref`): + +``` +[Flatpak Ref] +Title=Dummy App$NUM +Name=com.dummy.App$NUM +Branch=stable +Url=file:///tmp/flatpak/repo +GPGKey={{ base64-encoded public KEY }} +IsRuntime=false +RuntimeRepo=https://flathub.org/repo/flathub.flatpakrepo +``` + +Add a `.flatpakrepo` file to the `repo` folder (`repo/dummy-repo.flatpakrepo`): + +``` +[Flatpak Repo] +Title=Dummy Repo +Url=file:///tmp/flatpak/repo +Comment=Dummy repo for ansible module integration testing +Description=Dummy repo for ansible module integration testing +GPGKey={{ base64-encoded public KEY }} +``` + +## Sign the repo + +Create a new key in a new gpg home folder (On RedHat systems, the executable needs to addressed as gpg2): + +``` +mkdir gpg +gpg --homedir gpg --quick-gen-key test@dummy.com +``` + +Sign the repo and summary file, you need to redo this when you update the repository: + +``` +flatpak build-sign repo --gpg-sign=KEY_ID --gpg-homedir=gpg +flatpak build-update-repo repo --gpg-sign=KEY_ID --gpg-homedir=gpg +``` + +Export the public key as a file: + +``` +gpg --homedir=gpg --export KEY_ID > dummy-repo.gpg +``` + +Create base64-encoded string from gpg-file for `GPGKey=` property in flatpak*-files: + +``` +base64 dummy-repo.gpg | tr -d '\n' +``` + +## How to use the repo + +Now you can add the `repo` folder as a local repo: + +``` +flatpak --system remote-add --gpg-import=/tmp/flatpak/repo/dummy-repo.gpg dummy-repo /tmp/flatpak/repo +``` + +Or, via `.flatpakrepo` file: + +``` +flatpak --system remote-add dummy-repo /tmp/flatpak/repo/dummy-repo.flatpakrepo +``` + +And install the hello world flatpaks like this: + +``` +flatpak --system install dummy-repo com.dummy.App$NUM +``` + +Or from flatpakref: + +``` +flatpak --system install --from /tmp/flatpak/repo/com.dummy.App$NUM.flatpakref +``` + +Run the app: + +``` +flatpak run com.dummy.App$NUM +``` + +To install an app without any runtime dependencies (the app will be broken, but it is enough to test flatpak installation): + +``` +flatpak --system install --no-deps dummy-repo com.dummy.App$NUM +``` + +## Sources: + +* https://blogs.gnome.org/alexl/2017/02/10/maintaining-a-flatpak-repository/ + +* http://docs.flatpak.org/en/latest/first-build.html diff --git a/ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/create-repo.sh b/ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/create-repo.sh new file mode 100755 index 000000000..11c762184 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_flatpak_remote/create-repo.sh @@ -0,0 +1,63 @@ +#!/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 + +# Delete traces from last run +rm -rf appdir* dummy-repo.gpg gpg hello.sh repo + +# Create GPG key +mkdir -p gpg +chmod 0700 gpg +gpg --homedir gpg --batch --passphrase '' --quick-gen-key test@dummy.com future-default default 10y +KEY_ID=$(gpg --homedir=gpg --list-keys --with-colons test@dummy.com | grep fpr: | head -1 | cut -d ':' -f 10) +gpg --homedir=gpg --export "${KEY_ID}" > dummy-repo.gpg +BASE64_PUBLIC_KEY=$(base64 dummy-repo.gpg | tr -d '\n') + +# Install dependencies +flatpak install -y --system flathub org.freedesktop.Platform//1.6 org.freedesktop.Sdk//1.6 + +# Add individual flatpaks +echo $'#!/bin/sh\necho hello world' > hello.sh + +for NUM in 1 2 3; do + flatpak build-init appdir${NUM} com.dummy.App${NUM} org.freedesktop.Sdk org.freedesktop.Platform 1.6; + flatpak build appdir${NUM} mkdir /app/bin; + flatpak build appdir${NUM} install --mode=750 hello.sh /app/bin; + flatpak build-finish --command=hello.sh appdir${NUM} + + flatpak build-export repo appdir${NUM} stable + + cat > repo/com.dummy.App${NUM}.flatpakref < repo/dummy-repo.flatpakrepo <- + {{ + ansible_os_family not in ['Darwin', 'FreeBSD'] + and not (ansible_distribution == "CentOS" and ansible_distribution_version is version("7.0", "<")) + }} + +- name: Include OS-specific variables + include_vars: '{{ ansible_os_family }}.yml' + when: has_java_keytool + +- name: Install keytool + package: + name: '{{ keytool_package_name }}' + 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 new file mode 100644 index 000000000..4ff75ae8c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.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 + +keytool_package_name: 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 new file mode 100644 index 000000000..9e29065b3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.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 + +keytool_package_name: jre11-openjdk-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 new file mode 100644 index 000000000..30ae5cd04 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.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 + +keytool_package_name: 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 new file mode 100644 index 000000000..c200091f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.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 + +keytool_package_name: 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 new file mode 100644 index 000000000..c200091f8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.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 + +keytool_package_name: java-11-openjdk-headless diff --git a/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/files/mosquitto.conf b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/files/mosquitto.conf new file mode 100644 index 000000000..450330293 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/files/mosquitto.conf @@ -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 + +# Plain MQTT protocol +listener 1883 + +# MQTT over TLS 1.1 +listener 8883 +tls_version tlsv1.1 +cafile /tls/ca_certificate.pem +certfile /tls/server_certificate.pem +keyfile /tls/server_key.pem + +# MQTT over TLS 1.2 +listener 8884 +tls_version tlsv1.2 +cafile /tls/ca_certificate.pem +certfile /tls/server_certificate.pem +keyfile /tls/server_key.pem + +# TODO(This does not appear to be supported on Ubuntu 18.04. Re-try on 20.04 or next LTS release) +# MQTT over TLS 1.3 +# +# listener 8885 +# tls_version tlsv1.3 +# cafile /tls/ca_certificate.pem +# certfile /tls/server_certificate.pem +# keyfile /tls/server_key.pem + +log_dest syslog + +log_type error +log_type warning +log_type notice +log_type information +log_type debug + +connection_messages true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/meta/main.yml new file mode 100644 index 000000000..488c355d0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/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_tls diff --git a/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/main.yml new file mode 100644 index 000000000..2dd0674dc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/main.yml @@ -0,0 +1,12 @@ +--- +#################################################################### +# 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 + +- include_tasks: ubuntu.yml + when: ansible_distribution == 'Ubuntu' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/ubuntu.yml b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/ubuntu.yml new file mode 100644 index 000000000..8222aa175 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_mosquitto/tasks/ubuntu.yml @@ -0,0 +1,29 @@ +--- +# 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 https transport for apt + apt: + name: apt-transport-https + state: latest + force: true + +- name: Install Mosquitto Server + apt: + name: mosquitto + state: latest + register: result + until: result is success + delay: 3 + retries: 10 + +- name: Ensure TLS config + copy: + src: mosquitto.conf + dest: /etc/mosquitto/mosquitto.conf + +- name: Start Mosquitto service + service: + name: mosquitto + state: restarted 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 new file mode 100644 index 000000000..8f8c537bd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif @@ -0,0 +1,22 @@ +dn: ou=users,dc=example,dc=com +objectClass: organizationalUnit +objectClass: top +ou: users + +dn: uid=ldaptest,ou=users,dc=example,dc=com +uid: ldaptest +uidNumber: 1111 +gidNUmber: 100 +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: person +objectClass: organizationalPerson +objectClass: inetOrgPerson +loginShell: /bin/sh +homeDirectory: /home/ldaptest +cn: LDAP Test +gecos: LDAP Test +displayName: LDAP Test +mail: ldap.test@example.com +sn: Test diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif.license b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif new file mode 100644 index 000000000..7fb34d93b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif @@ -0,0 +1,4 @@ +dn: olcDatabase={0}config,cn=config +changetype: modify +replace: olcRootPW +olcRootPW: "Test1234!" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif.license b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/rootpw_cnconfig.ldif.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_openldap/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/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/setup_openldap/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml new file mode 100644 index 000000000..25077de16 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml @@ -0,0 +1,72 @@ +--- +#################################################################### +# 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: Setup OpenLDAP on Debian or Ubuntu + block: + - name: Include OS-specific variables + include_vars: '{{ ansible_os_family }}.yml' + + - name: Install OpenLDAP server and tools + become: true + package: + name: '{{ item }}' + loop: '{{ openldap_packages_name }}' + + - name: Install python-ldap (Python 3) + become: true + package: + name: '{{ python_ldap_package_name_python3 }}' + when: ansible_python_version is version('3.0', '>=') + + - name: Install python-ldap (Python 2) + become: true + package: + name: '{{ python_ldap_package_name }}' + when: ansible_python_version is version('3.0', '<') + + - name: Make sure OpenLDAP service is stopped + become: true + shell: 'cat /var/run/slapd/slapd.pid | xargs -r kill -9 ' + + - name: Debconf + shell: 'echo "slapd {{ item.question }} {{ item.vtype }} {{ item.value }}" >> /root/debconf-slapd.conf' + loop: "{{ openldap_debconfs }}" + + - name: Dpkg reconfigure + shell: + cmd: "export DEBIAN_FRONTEND=noninteractive; cat /root/debconf-slapd.conf | debconf-set-selections; dpkg-reconfigure -f noninteractive slapd" + creates: "/root/slapd_configured" + + - name: Start OpenLDAP service + become: true + service: + name: '{{ openldap_service_name }}' + enabled: true + state: started + + - name: Copy initial config ldif file + become: true + copy: + src: 'files/{{ item }}' + dest: '/tmp/{{ item }}' + owner: root + group: root + mode: '0644' + loop: + - rootpw_cnconfig.ldif + - initial_config.ldif + + - name: Configure admin password for cn=config + shell: "ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/rootpw_cnconfig.ldif" + + - name: Add initial config + become: true + shell: 'ldapadd -H ldapi:/// -x -D "cn=admin,dc=example,dc=com" -w Test1234! -f /tmp/initial_config.ldif' + when: ansible_os_family in ['Ubuntu', 'Debian'] diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Debian.yml new file mode 100644 index 000000000..3b4f19810 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Debian.yml @@ -0,0 +1,60 @@ +--- +# 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 + +python_ldap_package_name: python-ldap +python_ldap_package_name_python3: python3-ldap +openldap_packages_name: + - slapd + - ldap-utils +openldap_service_name: slapd +openldap_debconfs: + - question: "shared/organization" + value: "Example Organization" + vtype: "string" + - question: "slapd/allow_ldap_v2" + value: "false" + vtype: "boolean" + - question: "slapd/backend" + value: "MDB" + vtype: "select" + - question: "slapd/domain" + value: "example.com" + vtype: "string" + - question: "slapd/dump_database" + value: "when needed" + vtype: "select" + - question: "slapd/dump_database_destdir" + value: "/var/backups/slapd-VERSION" + vtype: "string" + - question: "slapd/internal/adminpw" + value: "Test1234!" + vtype: "password" + - question: "slapd/internal/generated_adminpw" + value: "Test1234!" + vtype: "password" + - question: "slapd/invalid_config" + value: "true" + vtype: "boolean" + - question: "slapd/move_old_database" + value: "true" + vtype: "boolean" + - question: "slapd/no_configuration" + value: "false" + vtype: "boolean" + - question: "slapd/password1" + value: "Test1234!" + vtype: "password" + - question: "slapd/password2" + value: "Test1234!" + vtype: "password" + - question: "slapd/password_mismatch" + value: "" + vtype: "note" + - question: "slapd/purge_database" + value: "false" + vtype: "boolean" + - question: "slapd/upgrade_slapcat_failure" + value: "" + vtype: "error" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Ubuntu.yml b/ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Ubuntu.yml new file mode 100644 index 000000000..3b4f19810 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/vars/Ubuntu.yml @@ -0,0 +1,60 @@ +--- +# 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 + +python_ldap_package_name: python-ldap +python_ldap_package_name_python3: python3-ldap +openldap_packages_name: + - slapd + - ldap-utils +openldap_service_name: slapd +openldap_debconfs: + - question: "shared/organization" + value: "Example Organization" + vtype: "string" + - question: "slapd/allow_ldap_v2" + value: "false" + vtype: "boolean" + - question: "slapd/backend" + value: "MDB" + vtype: "select" + - question: "slapd/domain" + value: "example.com" + vtype: "string" + - question: "slapd/dump_database" + value: "when needed" + vtype: "select" + - question: "slapd/dump_database_destdir" + value: "/var/backups/slapd-VERSION" + vtype: "string" + - question: "slapd/internal/adminpw" + value: "Test1234!" + vtype: "password" + - question: "slapd/internal/generated_adminpw" + value: "Test1234!" + vtype: "password" + - question: "slapd/invalid_config" + value: "true" + vtype: "boolean" + - question: "slapd/move_old_database" + value: "true" + vtype: "boolean" + - question: "slapd/no_configuration" + value: "false" + vtype: "boolean" + - question: "slapd/password1" + value: "Test1234!" + vtype: "password" + - question: "slapd/password2" + value: "Test1234!" + vtype: "password" + - question: "slapd/password_mismatch" + value: "" + vtype: "note" + - question: "slapd/purge_database" + value: "false" + vtype: "boolean" + - question: "slapd/upgrade_slapcat_failure" + value: "" + vtype: "error" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_opennebula/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_opennebula/meta/main.yml new file mode 100644 index 000000000..fe9e33681 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_opennebula/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_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/setup_opennebula/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_opennebula/tasks/main.yml new file mode 100644 index 000000000..b7babbaab --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_opennebula/tasks/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: Install required library + pip: + name: pyone + extra_args: "-c {{ remote_constraints }}" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_opennebula/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_opennebula/vars/main.yml new file mode 100644 index 000000000..39b48270a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_opennebula/vars/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 + +opennebula_test: + hosts: + - hv1 + - hv2 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/meta/main.yml new file mode 100644 index 000000000..d4a5c7d05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/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_remote_constraints + - setup_pkg_mgr diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/tasks/main.yml new file mode 100644 index 000000000..b8e003710 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/tasks/main.yml @@ -0,0 +1,69 @@ +--- +#################################################################### +# 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: Include OS-specific variables + include_vars: '{{ lookup("first_found", search) }}' + vars: + search: + files: + - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml' + - '{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml' + - '{{ ansible_distribution }}.yml' + - '{{ ansible_os_family }}.yml' + - default.yml + paths: + - vars + +- name: Install OpenSSL + become: true + package: + name: '{{ openssl_package_name }}' + when: not ansible_os_family == 'Darwin' + +- when: ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['CentOS6', 'RedHat6'] + block: + - name: Install cryptography (Python 3) + become: true + package: + name: '{{ cryptography_package_name_python3 }}' + when: not cryptography_from_pip and ansible_python_version is version('3.0', '>=') + + - name: Install cryptography (Python 2) + become: true + package: + name: '{{ cryptography_package_name }}' + when: not cryptography_from_pip and ansible_python_version is version('3.0', '<') + + - name: Install cryptography (pip) + become: true + pip: + name: cryptography>=3.3 + extra_args: "-c {{ remote_constraints }}" + when: cryptography_from_pip + +- name: Install pyOpenSSL (Python 3) + become: true + package: + name: '{{ pyopenssl_package_name_python3 }}' + when: pyopenssl_package_name_python3 is defined and ansible_python_version is version('3.0', '>=') + +- name: Install pyOpenSSL (Python 2) + become: true + package: + name: '{{ pyopenssl_package_name }}' + when: pyopenssl_package_name is defined and ansible_python_version is version('3.0', '<') + +- name: register openssl version + shell: "openssl version | cut -d' ' -f2" + register: openssl_version + +- name: register cryptography version + command: "{{ ansible_python.executable }} -c 'import cryptography; print(cryptography.__version__)'" + register: cryptography_version diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Alpine.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Alpine.yml new file mode 100644 index 000000000..c5d4d23a4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Alpine.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 + +cryptography_package_name: py-cryptography +cryptography_package_name_python3: py3-cryptography +pyopenssl_package_name: py-openssl +pyopenssl_package_name_python3: py3-openssl +openssl_package_name: openssl +cryptography_from_pip: false diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Archlinux.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Archlinux.yml new file mode 100644 index 000000000..b6ae2fe10 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Archlinux.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 + +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python-cryptography +pyopenssl_package_name: python-pyopenssl +pyopenssl_package_name_python3: python-pyopenssl +openssl_package_name: openssl +cryptography_from_pip: false diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/CentOS-8.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/CentOS-8.yml new file mode 100644 index 000000000..875a69718 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/CentOS-8.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 + +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography +openssl_package_name: openssl +cryptography_from_pip: '{{ ansible_python_version is version("3.8", ">=") }}' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Darwin.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Darwin.yml new file mode 100644 index 000000000..b3dbd9811 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Darwin.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 + +cryptography_from_pip: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Debian.yml new file mode 100644 index 000000000..6ef3df1a1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Debian.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 + +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography +pyopenssl_package_name: python-openssl +pyopenssl_package_name_python3: python3-openssl +openssl_package_name: openssl +cryptography_from_pip: false diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/FreeBSD.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/FreeBSD.yml new file mode 100644 index 000000000..e5ad6812f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/FreeBSD.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 + +cryptography_package_name: py27-cryptography +cryptography_package_name_python3: "py{{ ansible_python.version.major }}{{ ansible_python.version.minor }}-cryptography" +pyopenssl_package_name: py27-openssl +pyopenssl_package_name_python3: "py{{ ansible_python.version.major }}{{ ansible_python.version.minor }}-openssl" +openssl_package_name: openssl +cryptography_from_pip: false diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat-9.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat-9.yml new file mode 100644 index 000000000..ac9b3344e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat-9.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 + +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography +openssl_package_name: openssl +cryptography_from_pip: false diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat.yml new file mode 100644 index 000000000..ef78bab01 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/RedHat.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 + +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography +pyopenssl_package_name: pyOpenSSL +pyopenssl_package_name_python3: python3-pyOpenSSL +openssl_package_name: openssl +cryptography_from_pip: false diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Suse.yml b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Suse.yml new file mode 100644 index 000000000..b7d246653 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_openssl/vars/Suse.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 + +cryptography_package_name: python-cryptography +cryptography_package_name_python3: python3-cryptography +pyopenssl_package_name: python-pyOpenSSL +pyopenssl_package_name_python3: python3-pyOpenSSL +openssl_package_name: openssl +cryptography_from_pip: false 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 new file mode 100644 index 000000000..fc75f84df --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.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 + +# Since Arch Linux is a rolling distribution, it regularly needs its packages upgraded, otherwise some tests might +# stop working due to conflicts during package installation. Since there is no good way to do this on container +# startup time, we use the setup_pkg_mgr setup role to do this once per CI run (hopefully). In case the Arch Linux +# tests are run outside of a container, we're using a date-based tag (see below) to avoid this running more than +# once per day. + +- name: Create tag + copy: + dest: /tmp/.ansible_archlinux_sysupgrade_tag + content: | + Last ArchLinux system upgrade by integration tests was done on {{ ansible_facts.date_time.date }}. + register: archlinux_upgrade_tag + +- name: Upgrade all packages + pacman: + update_cache: true + upgrade: true + when: archlinux_upgrade_tag is changed diff --git a/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/main.yml new file mode 100644 index 000000000..5bff53b3b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/main.yml @@ -0,0 +1,39 @@ +--- +#################################################################### +# 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 + +- set_fact: + pkg_mgr: community.general.pkgng + ansible_pkg_mgr: community.general.pkgng + cacheable: true + when: ansible_os_family == "FreeBSD" + +- set_fact: + pkg_mgr: community.general.zypper + ansible_pkg_mgr: community.general.zypper + cacheable: true + when: ansible_os_family == "Suse" + +- set_fact: + pkg_mgr: community.general.pacman + ansible_pkg_mgr: community.general.pacman + cacheable: true + when: ansible_os_family == "Archlinux" + +- shell: + cmd: | + sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-*.repo + sed -i 's%#baseurl=http://mirror.centos.org/$contentdir/$releasever/%baseurl=https://vault.centos.org/8.4.2105/%g' /etc/yum.repos.d/CentOS-Linux-*.repo + ignore_errors: true # This fails for CentOS Stream 8 + when: ansible_distribution in 'CentOS' and ansible_distribution_major_version == '8' + +- when: ansible_os_family == "Archlinux" + block: + - name: ArchLinux specific setup + include_tasks: archlinux.yml diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/defaults/main.yml new file mode 100644 index 000000000..1a33ecafa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/defaults/main.yml @@ -0,0 +1,22 @@ +--- +# 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_service: postgresql + +postgresql_packages: + - postgresql-server + - python-psycopg2 + +pg_user: postgres +pg_group: root + +locale_latin_suffix: +locale_utf8_suffix: + +# defaults for test SSL +ssl_db: 'ssl_db' +ssl_user: 'ssl_user' +ssl_pass: 'ssl_pass' +ssl_rootcert: '~{{ pg_user }}/root.crt' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql new file mode 100644 index 000000000..89b318d77 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--1.0.sql @@ -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 + +CREATE OR REPLACE FUNCTION dummy_display_ext_version() +RETURNS text LANGUAGE SQL AS 'SELECT (''1.0'')::text'; diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql new file mode 100644 index 000000000..c9386cac4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--2.0.sql @@ -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 + +CREATE OR REPLACE FUNCTION dummy_display_ext_version() +RETURNS text LANGUAGE SQL AS 'SELECT (''2.0'')::text'; diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql new file mode 100644 index 000000000..a96bc8587 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy--3.0.sql @@ -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 + +CREATE OR REPLACE FUNCTION dummy_display_ext_version() +RETURNS text LANGUAGE SQL AS 'SELECT (''3.0'')::text'; diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control new file mode 100644 index 000000000..4f8553c22 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control @@ -0,0 +1,3 @@ +comment = 'dummy extension used to test postgresql_ext Ansible module' +default_version = '3.0' +relocatable = true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control.license b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/dummy.control.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf new file mode 100644 index 000000000..e6b14c4d7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/files/pg_hba.conf @@ -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 + +# !!! This file managed by Ansible. Any local changes may be overwritten. !!! + +# Database administrative login by UNIX sockets +# note: you may wish to restrict this further later +local all {{ pg_user }} trust + +# TYPE DATABASE USER CIDR-ADDRESS METHOD +local all all md5 +host all all 127.0.0.1/32 md5 +host all all ::1/128 md5 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/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/setup_postgresql_db/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml new file mode 100644 index 000000000..3dac4a098 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml @@ -0,0 +1,257 @@ +--- +#################################################################### +# 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 + +# Exit when Suse because it causes CI problems +- meta: end_play + when: ansible_os_family == 'Suse' + +# To avoid hangings on service start/stop postgres during CI runs: +- meta: end_play + when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8' + +# Temporary disable Fedora 34 +- meta: end_play + when: ansible_facts.distribution == 'Fedora' and ansible_facts.distribution_major_version == '34' + +- name: python 2 + set_fact: + python_suffix: '' + when: ansible_python_version is version('3', '<') + +- name: python 3 + set_fact: + python_suffix: -py3 + when: ansible_python_version is version('3', '>=') + +- name: Include distribution and Python version specific variables + include_vars: '{{ lookup(''first_found'', params) }}' + vars: + params: + files: + - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml' + - '{{ ansible_distribution }}-{{ ansible_distribution_version }}{{ python_suffix }}.yml' + - '{{ ansible_os_family }}{{ python_suffix }}.yml' + - default{{ python_suffix }}.yml + paths: + - '{{ role_path }}/vars' + +- name: make sure the dbus service is started under systemd + systemd: + name: dbus + state: started + when: ansible_service_mgr == 'systemd' and ansible_distribution == 'Fedora' + +- name: Kill all postgres processes + shell: 'pkill -u {{ pg_user }}' + become: true + when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8' + ignore_errors: true + +- name: stop postgresql service + service: name={{ postgresql_service }} state=stopped + ignore_errors: true + +- name: remove old db (RedHat or Suse) + file: + path: '{{ pg_dir }}' + state: absent + ignore_errors: true + when: ansible_os_family == "RedHat" or ansible_os_family == "Suse" + +- name: remove old db (FreeBSD) + file: + path: '{{ pg_dir }}' + state: absent + ignore_errors: true + when: ansible_os_family == "FreeBSD" + +- name: remove old db config and files (debian) + file: + path: '{{ loop_item }}' + state: absent + ignore_errors: true + when: ansible_os_family == "Debian" + loop: + - /etc/postgresql + - /var/lib/postgresql + loop_control: + loop_var: loop_item + +- name: install dependencies for postgresql test + package: + name: '{{ postgresql_package_item }}' + state: present + with_items: '{{ postgresql_packages }}' + loop_control: + loop_var: postgresql_package_item + +- name: initialize postgres (FreeBSD) + command: /usr/local/etc/rc.d/postgresql oneinitdb + when: ansible_os_family == "FreeBSD" + +- name: Initialize postgres (RedHat systemd) + command: postgresql-setup initdb + when: ansible_os_family == "RedHat" and ansible_service_mgr == "systemd" + +- name: Initialize postgres (RedHat sysv) + command: /sbin/service postgresql initdb + when: ansible_os_family == "RedHat" and ansible_service_mgr != "systemd" + +- name: Initialize postgres (Archlinux) + command: su - postgres -c "initdb --locale en_US.UTF-8 -D '/var/lib/postgres/data'" + when: ansible_os_family == "Archlinux" + +- name: Initialize postgres (Alpine) + command: su - postgres -c "initdb --locale en_US.UTF-8 -D '/var/lib/postgresql/data'" + when: ansible_os_family == "Alpine" + +- name: Initialize postgres (Debian) + shell: . /usr/share/postgresql-common/maintscripts-functions && set_system_locale && /usr/bin/pg_createcluster -u postgres {{ pg_ver }} main + args: + creates: /etc/postgresql/{{ pg_ver }}/ + when: ansible_os_family == 'Debian' + +- name: Initialize postgres (Suse) + service: name=postgresql state=stopped + when: ansible_os_family == 'Suse' + +- name: Pause between stop and start postgresql + pause: + seconds: 5 + when: ansible_os_family == 'Suse' + +- name: Initialize postgres (Suse) + service: name=postgresql state=started + when: ansible_os_family == 'Suse' + +- name: Copy pg_hba into place + template: + src: files/pg_hba.conf + dest: '{{ pg_hba_location }}' + owner: '{{ pg_user }}' + group: '{{ pg_group }}' + mode: '0644' + +- name: Generate locales (Debian) + locale_gen: + name: '{{ item }}' + state: present + with_items: + - pt_BR + - es_ES + when: ansible_os_family == 'Debian' + +- block: + - name: Install langpacks (RHEL8) + yum: + name: + - glibc-langpack-es + - glibc-langpack-pt + - glibc-all-langpacks + state: present + when: ansible_distribution_major_version is version('8', '>=') + + - name: Check if locales need to be generated (RedHat) + shell: localedef --list-archive | grep -a -q '^{{ locale }}$' + register: locale_present + ignore_errors: true + with_items: + - es_ES + - pt_BR + loop_control: + loop_var: locale + + - name: Reinstall internationalization files + shell: yum -y reinstall glibc-common || yum -y install glibc-common + when: locale_present is failed + + - name: Generate locale (RedHat) + command: localedef -f ISO-8859-1 -i {{ item.locale }} {{ item.locale }} + when: item is failed + with_items: '{{ locale_present.results }}' + when: ansible_os_family == 'RedHat' and ansible_distribution != 'Fedora' + +- name: Install glibc langpacks (Fedora >= 24) + package: + name: '{{ item }}' + state: latest + with_items: + - glibc-langpack-es + - glibc-langpack-pt + when: ansible_distribution == 'Fedora' and ansible_distribution_major_version is version('24', '>=') + +- name: enable postgresql service (FreeBSD) + lineinfile: + path: /etc/rc.conf + line: postgresql_enable="YES" + when: ansible_os_family == "FreeBSD" + +- name: start postgresql service + service: name={{ postgresql_service }} state=started + +- name: Pause between start and stop + pause: + seconds: 5 + +- name: Kill all postgres processes + shell: 'pkill -u {{ pg_user }}' + become: true + when: ansible_facts.distribution == 'CentOS' and ansible_facts.distribution_major_version == '8' + ignore_errors: true + register: terminate + +- name: Stop postgresql service + service: name={{ postgresql_service }} state=stopped + when: terminate is not succeeded + +- name: Pause between stop and start + pause: + seconds: 5 + +- name: Start postgresql service + service: name={{ postgresql_service }} state=started + +- name: copy control file for dummy ext + copy: + src: dummy.control + dest: /usr/share/postgresql/{{ pg_ver }}/extension/dummy.control + mode: '0444' + when: ansible_os_family == 'Debian' + +- name: copy version files for dummy ext + copy: + src: '{{ item }}' + dest: /usr/share/postgresql/{{ pg_ver }}/extension/{{ item }} + mode: '0444' + with_items: + - dummy--1.0.sql + - dummy--2.0.sql + - dummy--3.0.sql + when: ansible_os_family == 'Debian' + +- name: add update paths + file: + path: /usr/share/postgresql/{{ pg_ver }}/extension/{{ item }} + mode: '0444' + state: touch + with_items: + - dummy--1.0--2.0.sql + - dummy--2.0--3.0.sql + when: ansible_os_family == 'Debian' + +- name: Get PostgreSQL version + become_user: '{{ pg_user }}' + become: true + shell: echo 'SHOW SERVER_VERSION' | psql --tuples-only --no-align --dbname postgres + register: postgres_version_resp + +- name: Print PostgreSQL server version + debug: + msg: '{{ postgres_version_resp.stdout }}' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Alpine-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Alpine-py3.yml new file mode 100644 index 000000000..99a8a14b8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Alpine-py3.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 + +postgresql_packages: + - "postgresql" + - "py3-psycopg2" + +pg_hba_location: "/var/lib/postgresql/data/pg_hba.conf" +pg_dir: "/var/lib/postgresql/data" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Archlinux-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Archlinux-py3.yml new file mode 100644 index 000000000..40215d1ad --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Archlinux-py3.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 + +postgresql_packages: + - "postgresql" + - "python-psycopg2" + +pg_hba_location: "/var/lib/postgres/data/pg_hba.conf" +pg_dir: "/var/lib/postgres/data" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-11-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-11-py3.yml new file mode 100644 index 000000000..1ffd257b2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-11-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/13/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/13/main" +pg_ver: 13 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-8.yml new file mode 100644 index 000000000..87063214a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-8.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" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.4/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.4/main" +pg_ver: 9.4 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11-py3.yml new file mode 100644 index 000000000..a92de47cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11-py3.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 + +postgresql_packages: + - postgresql95-server + - "py{{ ansible_python.version.major }}{{ ansible_python.version.minor }}-psycopg2" + +pg_dir: /usr/local/pgsql/data +pg_hba_location: "{{ pg_dir }}/pg_hba.conf" +pg_ver: 9.5 +pg_user: pgsql +pg_group: pgsql + +locale_latin_suffix: .ISO8859-1 +locale_utf8_suffix: .UTF-8 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11.yml new file mode 100644 index 000000000..c38f3b59b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-11.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 + +postgresql_packages: + - postgresql95-server + - py27-psycopg2 + +pg_dir: /usr/local/pgsql/data +pg_hba_location: "{{ pg_dir }}/pg_hba.conf" +pg_ver: 9.5 +pg_user: pgsql +pg_group: pgsql + +locale_latin_suffix: .ISO8859-1 +locale_utf8_suffix: .UTF-8 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0-py3.yml new file mode 100644 index 000000000..a92de47cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0-py3.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 + +postgresql_packages: + - postgresql95-server + - "py{{ ansible_python.version.major }}{{ ansible_python.version.minor }}-psycopg2" + +pg_dir: /usr/local/pgsql/data +pg_hba_location: "{{ pg_dir }}/pg_hba.conf" +pg_ver: 9.5 +pg_user: pgsql +pg_group: pgsql + +locale_latin_suffix: .ISO8859-1 +locale_utf8_suffix: .UTF-8 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0.yml new file mode 100644 index 000000000..7c5586f5b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.0.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 + +postgresql_packages: + - postgresql96-server + - py27-psycopg2 + +pg_dir: /usr/local/pgsql/data +pg_hba_location: "{{ pg_dir }}/pg_hba.conf" +pg_ver: 9.6 +pg_user: pgsql +pg_group: pgsql + +locale_latin_suffix: .ISO8859-1 +locale_utf8_suffix: .UTF-8 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1-py3.yml new file mode 100644 index 000000000..8e5ddfa13 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1-py3.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 + +postgresql_packages: + - postgresql11-server + - "py{{ ansible_python.version.major }}{{ ansible_python.version.minor }}-psycopg2" + +pg_dir: /var/db/postgres/data11 +pg_hba_location: "{{ pg_dir }}/pg_hba.conf" +pg_ver: 11 +pg_user: postgres +pg_group: postgres + +locale_latin_suffix: .ISO8859-1 +locale_utf8_suffix: .UTF-8 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1.yml new file mode 100644 index 000000000..0cdc22e13 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/FreeBSD-12.1.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 + +postgresql_packages: + - postgresql11-server + - py27-psycopg2 + +pg_dir: /var/db/postgres/data11 +pg_hba_location: "{{ pg_dir }}/pg_hba.conf" +pg_ver: 11 +pg_user: postgres +pg_group: postgres + +locale_latin_suffix: .ISO8859-1 +locale_utf8_suffix: .UTF-8 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat-py3.yml new file mode 100644 index 000000000..3892f2e45 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat-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-server" + - "python3-psycopg2" + - "bzip2" + - "xz" + +pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" +pg_dir: "/var/lib/pgsql/data" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml new file mode 100644 index 000000000..5670a7fc9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/RedHat.yml @@ -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 + +postgresql_packages: + - "postgresql-server" + - "python-psycopg2" + - "bzip2" + +pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" +pg_dir: "/var/lib/pgsql/data" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-12.yml new file mode 100644 index 000000000..1d850de84 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-12.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" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.1/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.1/main" +pg_ver: 9.1 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-14.yml new file mode 100644 index 000000000..881fc533e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-14.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" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.3/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.3/main" +pg_ver: 9.3 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16-py3.yml new file mode 100644 index 000000000..482982fe1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16-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/9.5/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.5/main" +pg_ver: 9.5 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16.yml new file mode 100644 index 000000000..f2df72af7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-16.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" + - "python-psycopg2" + +pg_hba_location: "/etc/postgresql/9.5/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/9.5/main" +pg_ver: 9.5 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-18-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-18-py3.yml new file mode 100644 index 000000000..19213f4d6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-18-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/10/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/10/main" +pg_ver: 10 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-py3.yml new file mode 100644 index 000000000..58fb8a06f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-20-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/12/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/12/main" +pg_ver: 12 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-22-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-22-py3.yml new file mode 100644 index 000000000..8ea444099 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Ubuntu-22-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/14/main/pg_hba.conf" +pg_dir: "/var/lib/postgresql/14/main" +pg_ver: 14 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default-py3.yml new file mode 100644 index 000000000..6f96043a3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default-py3.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 + +postgresql_packages: + - "postgresql-server" + - "python3-psycopg2" + +pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" +pg_dir: "/var/lib/pgsql/data" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default.yml new file mode 100644 index 000000000..9d64d969a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/default.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 + +postgresql_packages: + - "postgresql-server" + - "python-psycopg2" + +pg_hba_location: "/var/lib/pgsql/data/pg_hba.conf" +pg_dir: "/var/lib/pgsql/data" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/defaults/main.yml new file mode 100644 index 000000000..46dae9898 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/defaults/main.yml @@ -0,0 +1,56 @@ +--- +# 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 + +# General +redis_packages: + Alpine: + - redis + Archlinux: + - redis + Debian: + - redis-server + Ubuntu: + - redis-server + openSUSE Leap: + - redis + Fedora: + - redis + CentOS: + - redis + FreeBSD: + - redis + +redis_bin: + Alpine: /usr/bin/redis-server + Archlinux: /usr/bin/redis-server + Debian: /usr/bin/redis-server + Ubuntu: /usr/bin/redis-server + openSUSE Leap: /usr/sbin/redis-server + Fedora: /usr/bin/redis-server + CentOS: /usr/bin/redis-server + FreeBSD: /usr/local/bin/redis-server + +redis_module: redis + +redis_password: PASS + +old_redis: >- + {{ + (ansible_distribution == 'CentOS' and ansible_distribution_major_version|int <= 7) or + (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version|int <= 18) or + (ansible_os_family == 'FreeBSD' and ansible_distribution_major_version|int <= 12) + }} + +# Master +master_port: 6379 +master_conf: /etc/redis-master.conf +master_datadir: /var/lib/redis-master +master_logdir: /var/log/redis-master + +# Replica +replica_port: 6380 +replica_conf: /etc/redis-replica.conf +replica_datadir: /var/lib/redis-replica +replica_logdir: /var/log/redis-replica diff --git a/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/handlers/main.yml new file mode 100644 index 000000000..a0595cbe3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/handlers/main.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 + +- name: stop redis services + shell: | + kill -TERM $(cat /var/run/redis_{{ master_port }}.pid) + kill -TERM $(cat /var/run/redis_{{ replica_port }}.pid) + listen: cleanup redis + +- name: remove redis packages + action: "{{ ansible_facts.pkg_mgr }}" + args: + name: "{{ item }}" + state: absent + loop: "{{ redis_packages[ansible_distribution] }}" + listen: cleanup redis + +- name: remove pip packages + pip: + name: redis + state: absent + listen: cleanup redis + +- name: remove redis data + file: + path: "{{ item }}" + state: absent + loop: + - "{{ master_conf }}" + - "{{ master_datadir }}" + - "{{ master_logdir }}" + - /var/run/redis_{{ master_port }}.pid + - "{{ replica_conf }}" + - "{{ replica_datadir }}" + - "{{ replica_logdir }}" + - /var/run/redis_{{ replica_port }}.pid + listen: cleanup redis diff --git a/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/meta/main.yml new file mode 100644 index 000000000..db2617f4c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/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_constraints diff --git a/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/main.yml new file mode 100644 index 000000000..076a47359 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/main.yml @@ -0,0 +1,12 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2020, Pavlo Bashynskyi (@levonet) +# 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_redis_cluster.yml + when: + - ansible_distribution in ['CentOS', 'Fedora', 'FreeBSD', 'openSUSE Leap', 'Ubuntu', 'Debian', 'Archlinux', 'Alpine'] diff --git a/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/setup_redis_cluster.yml b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/setup_redis_cluster.yml new file mode 100644 index 000000000..dd48bf2b6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_redis_replication/tasks/setup_redis_cluster.yml @@ -0,0 +1,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 + +# We run two servers listening different ports +# to be able to check replication (one server for master, another for replica). + +- name: Install redis dependencies + package: + name: "{{ redis_packages[ansible_distribution] }}" + state: latest + policy_rc_d: "{{ 101 if ansible_facts.pkg_mgr == 'apt' else omit }}" + notify: cleanup redis + +- name: Install redis module + pip: + name: "{{ redis_module }}" + extra_args: "-c {{ remote_constraints }}" + state: present + notify: cleanup redis + +- name: Create redis directories + file: + path: "{{ item }}" + state: directory + owner: redis + group: redis + loop: + - "{{ master_datadir }}" + - "{{ master_logdir }}" + - "{{ replica_datadir }}" + - "{{ replica_logdir }}" + +- name: Create redis configs + copy: + dest: "{{ item.file }}" + content: | + daemonize yes + port {{ item.port }} + pidfile /var/run/redis_{{ item.port }}.pid + logfile {{ item.logdir }}/redis.log + dir {{ item.datadir }} + requirepass {{ redis_password }} + masterauth {{ redis_password }} + loop: + - file: "{{ master_conf }}" + port: "{{ master_port }}" + logdir: "{{ master_logdir }}" + datadir: "{{ master_datadir }}" + - file: "{{ replica_conf }}" + port: "{{ replica_port }}" + logdir: "{{ replica_logdir }}" + datadir: "{{ replica_datadir }}" + +- name: Start redis master + shell: "{{ redis_bin[ansible_distribution] }} {{ master_conf }}" + +- name: Start redis replica + shell: "{{ redis_bin[ansible_distribution] }} {{ replica_conf }} --{% if old_redis %}slaveof{% else %}replicaof{% endif %} 127.0.0.1 {{ master_port }}" + +- name: Wait for redis master to be started + ansible.builtin.wait_for: + host: 127.0.0.1 + port: "{{ master_port }}" + state: started + delay: 1 + connect_timeout: 5 + timeout: 30 + +- name: Wait for redis replica to be started + ansible.builtin.wait_for: + host: 127.0.0.1 + port: "{{ replica_port }}" + state: started + delay: 1 + connect_timeout: 5 + timeout: 30 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/aliases b/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/aliases new file mode 100644 index 000000000..27ce6b087 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/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 + +needs/file/tests/utils/constraints.txt diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/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/setup_remote_constraints/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/tasks/main.yml new file mode 100644 index 000000000..a1ac1aead --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_constraints/tasks/main.yml @@ -0,0 +1,18 @@ +--- +#################################################################### +# 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: record constraints.txt path on remote host + set_fact: + remote_constraints: "{{ remote_tmp_dir }}/constraints.txt" + +- name: copy constraints.txt to remote host + copy: + src: "{{ role_path }}/../../../utils/constraints.txt" + dest: "{{ remote_constraints }}" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml new file mode 100644 index 000000000..f1c55b04f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/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: delete temporary directory + include_tasks: default-cleanup.yml + +- name: delete temporary directory (windows) + include_tasks: windows-cleanup.yml diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml new file mode 100644 index 000000000..cc74b70af --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.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: delete temporary directory + file: + path: "{{ remote_tmp_dir }}" + state: absent + no_log: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml new file mode 100644 index 000000000..c9d871c69 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/default.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 + +- name: create temporary directory + tempfile: + state: directory + suffix: .test + register: remote_tmp_dir + notify: + - delete temporary directory + +- name: record temporary directory + set_fact: + remote_tmp_dir: "{{ remote_tmp_dir.path }}" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml new file mode 100644 index 000000000..6632cc848 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml @@ -0,0 +1,20 @@ +--- +#################################################################### +# 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: make sure we have the ansible_os_family and ansible_distribution_version facts + setup: + gather_subset: distribution + when: ansible_facts == {} + +- include_tasks: "{{ lookup('first_found', files)}}" + vars: + files: + - "{{ ansible_os_family | lower }}.yml" + - "default.yml" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/handlers/main.yml new file mode 100644 index 000000000..f1c55b04f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/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: delete temporary directory + include_tasks: default-cleanup.yml + +- name: delete temporary directory (windows) + include_tasks: windows-cleanup.yml diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default-cleanup.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default-cleanup.yml new file mode 100644 index 000000000..cc74b70af --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default-cleanup.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: delete temporary directory + file: + path: "{{ remote_tmp_dir }}" + state: absent + no_log: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default.yml new file mode 100644 index 000000000..0aef57f99 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/default.yml @@ -0,0 +1,22 @@ +--- +# 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 ~/tmp + file: + path: '~/tmp' + state: directory + +- name: create temporary directory + tempfile: + state: directory + suffix: .test + path: ~/tmp + register: remote_tmp_dir + notify: + - delete temporary directory + +- name: record temporary directory + set_fact: + remote_tmp_dir: "{{ remote_tmp_dir.path }}" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/main.yml new file mode 100644 index 000000000..6632cc848 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_remote_tmp_dir_outside_tmp/tasks/main.yml @@ -0,0 +1,20 @@ +--- +#################################################################### +# 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: make sure we have the ansible_os_family and ansible_distribution_version facts + setup: + gather_subset: distribution + when: ansible_facts == {} + +- include_tasks: "{{ lookup('first_found', files)}}" + vars: + files: + - "{{ ansible_os_family | lower }}.yml" + - "default.yml" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_rundeck/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/defaults/main.yml new file mode 100644 index 000000000..c842901c0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/defaults/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 + +rundeck_war_url: https://packagecloud.io/pagerduty/rundeck/packages/java/org.rundeck/rundeck-3.4.4-20210920.war/artifacts/rundeck-3.4.4-20210920.war/download +rundeck_cli_url: https://github.com/rundeck/rundeck-cli/releases/download/v1.3.10/rundeck-cli-1.3.10-all.jar diff --git a/ansible_collections/community/general/tests/integration/targets/setup_rundeck/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/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/setup_rundeck/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/tasks/main.yml new file mode 100644 index 000000000..ea8b35f65 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/tasks/main.yml @@ -0,0 +1,41 @@ +--- +#################################################################### +# 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: Skip unsupported platforms + meta: end_play + when: ansible_distribution not in ['CentOS', 'Fedora', 'Debian', 'Ubuntu'] + +- name: Include OS-specific variables + include_vars: '{{ ansible_os_family }}.yml' + when: ansible_os_family in ['Debian', 'RedHat'] + +- name: Set Rundeck base dir + set_fact: + rdeck_base: /home/rundeck + +- name: Install OpenJDK + package: + name: "{{ openjdk_pkg }}" + state: present + +- name: Install Rundeck + shell: | + mkdir -p $RDECK_BASE; + curl -k -o $RDECK_BASE/rundeck.war -L '{{ rundeck_war_url }}'; + curl -k -o $RDECK_BASE/rundeck-cli.jar -L '{{ rundeck_cli_url }}' + cd $RDECK_BASE; + java -Xmx4g -jar rundeck.war & + environment: + RDECK_BASE: "{{ rdeck_base }}" + +- name: Wait for Rundeck port 4440 + wait_for: + host: localhost + port: 4440 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Alpine.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Alpine.yml new file mode 100644 index 000000000..dbf16b747 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Alpine.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 + +openjdk_pkg: openjdk11-jre-headless diff --git a/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Archlinux.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Archlinux.yml new file mode 100644 index 000000000..fd4429c0b --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Archlinux.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 + +openjdk_pkg: jre11-openjdk-headless diff --git a/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Debian.yml new file mode 100644 index 000000000..1407cede6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/Debian.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 + +openjdk_pkg: openjdk-11-jre-headless diff --git a/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/RedHat.yml new file mode 100644 index 000000000..314f0ef41 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_rundeck/vars/RedHat.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 + +openjdk_pkg: java-1.8.0-openjdk diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/aliases b/ansible_collections/community/general/tests/integration/targets/setup_snap/aliases new file mode 100644 index 000000000..0a430dff1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/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 + +needs/target/setup_epel diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/defaults/main.yml new file mode 100644 index 000000000..7b4ab8dc7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/defaults/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 + +has_snap: false + +snap_packages: + - snapd diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/handlers/main.yml new file mode 100644 index 000000000..08c1a23f1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/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 snapd + package: + name: "{{ snap_packages }}" + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/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/setup_snap/tasks/D-Fedora.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-Fedora.yml new file mode 100644 index 000000000..d0681fa38 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-Fedora.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 + +- name: Install snapd (default) + package: + name: "{{ snap_packages }}" + state: present + notify: Remove snapd + +- name: Make sure that snapd is running + service: + name: snapd + state: started + +- name: Create link /snap + file: + src: /var/lib/snapd/snap + dest: /snap + state: link + +- name: Inform that snap is installed + set_fact: + has_snap: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.2.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.2.yml new file mode 100644 index 000000000..5bbfaff12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.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-8.3.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.3.yml new file mode 100644 index 000000000..5bbfaff12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-8.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_snap/tasks/D-RedHat-9.0.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.0.yml new file mode 100644 index 000000000..5bbfaff12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.0.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.1.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.1.yml new file mode 100644 index 000000000..5bbfaff12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.1.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-Ubuntu.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-Ubuntu.yml new file mode 100644 index 000000000..93958d38d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-Ubuntu.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 + +- name: Install snapd (ubuntu) + package: + name: "{{ snap_packages }}" + state: present + notify: Remove snapd + +- name: Make sure that snapd is running + service: + name: snapd + state: started + +- name: Inform that snap is installed + set_fact: + has_snap: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/Debian.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/Debian.yml new file mode 100644 index 000000000..d0681fa38 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/Debian.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 + +- name: Install snapd (default) + package: + name: "{{ snap_packages }}" + state: present + notify: Remove snapd + +- name: Make sure that snapd is running + service: + name: snapd + state: started + +- name: Create link /snap + file: + src: /var/lib/snapd/snap + dest: /snap + state: link + +- name: Inform that snap is installed + set_fact: + has_snap: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/RedHat.yml new file mode 100644 index 000000000..d0681fa38 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/RedHat.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 + +- name: Install snapd (default) + package: + name: "{{ snap_packages }}" + state: present + notify: Remove snapd + +- name: Make sure that snapd is running + service: + name: snapd + state: started + +- name: Create link /snap + file: + src: /var/lib/snapd/snap + dest: /snap + state: link + +- name: Inform that snap is installed + set_fact: + has_snap: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/default.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/default.yml new file mode 100644 index 000000000..d0681fa38 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/default.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 + +- name: Install snapd (default) + package: + name: "{{ snap_packages }}" + state: present + notify: Remove snapd + +- name: Make sure that snapd is running + service: + name: snapd + state: started + +- name: Create link /snap + file: + src: /var/lib/snapd/snap + dest: /snap + state: link + +- name: Inform that snap is installed + set_fact: + has_snap: true diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/main.yml new file mode 100644 index 000000000..8f3744a70 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/main.yml @@ -0,0 +1,34 @@ +--- +#################################################################### +# 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: Print information on which we distinguish + debug: + msg: "Distribution '{{ ansible_facts.distribution }}', version '{{ ansible_facts.distribution_version }}', OS family '{{ ansible_facts.os_family }}'" + +- name: Install EPEL repository (RHEL only) + include_role: + name: setup_epel + when: + - ansible_distribution in ['RedHat', 'CentOS'] + - ansible_distribution_major_version is version('9', '<') + +- name: Include distribution specific tasks + include_tasks: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - "D-{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_version }}.yml" + - "D-{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml" + - "{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml" + - "D-{{ ansible_facts.distribution }}.yml" + - "{{ ansible_facts.os_family }}.yml" + - "nothing.yml" + paths: + - "{{ role_path }}/tasks" diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/nothing.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/nothing.yml new file mode 100644 index 000000000..5bbfaff12 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/nothing.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 new file mode 100644 index 000000000..130e0a2da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem @@ -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 + +-----BEGIN CERTIFICATE----- +MIIDAjCCAeqgAwIBAgIJANguFROhaWocMA0GCSqGSIb3DQEBCwUAMDExIDAeBgNV +BAMMF1RMU0dlblNlbGZTaWduZWR0Um9vdENBMQ0wCwYDVQQHDAQkJCQkMB4XDTE5 +MDExMTA4MzMxNVoXDTI5MDEwODA4MzMxNVowMTEgMB4GA1UEAwwXVExTR2VuU2Vs +ZlNpZ25lZHRSb290Q0ExDTALBgNVBAcMBCQkJCQwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDqVt84czSxWnWW4Ng6hmKE3NarbLsycwtjrYBokV7Kk7Mp +7PrBbYF05FOgSdJLvL6grlRSQK2VPsXdLfEv5uFXX6gyd2WQwKCiGGf4UY4ZIl4l +JVpSDsBV2orR4pOIf1s1+iSwvcRQkX46SVjoKWbDUc4VLo1uy8UvavQI+DMioYyy +0K2MbRs7oG2rdKks8zisfT0ymKnrFTdVeUjIrg0sStaMnf9VVkcEeYkfNY0vWqdn +CV5wPfDBlnnxGMgqGdLSpzfyJ7qafFET+q+gOvjsEqzn7DvlPkmk86hIIWXKi3aM +A9swknL3rnagJL6GioWRpYUwKdRKmZxdyr4I2JTTAgMBAAGjHTAbMAwGA1UdEwQF +MAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQACTpPBf5WSwZ7r +hrbPUN3qVh70HI0ZNK2jlK6b5fpSdw3JI/GQl0Kw3eGICLzwTByWvhD62U7IigL5 +0UWxWuEod310Y/qo/7OxRVPp5PH/0oNGoKHhEzas2ii0heQYGsHQUKGzYNNyVfjy +nqBFz5AcKf067LcXivYqod6JDQHqFq/5/hWlIsHHrZIeijqqtthPq39GlGAYO+AB +U66nzlH7YQgmfYfy6l7O4LsjXf/bz9rWvueO3NqCsmXV+FacDkOkwWA5Kf6rcgNL +3G+2HAVTRIXDnO4ShnK6aYMW+UklpYRlVYBBUOdwoNIp5gI+BlSc1IuF6PdLVt3q +VdjN1MjY +-----END CERTIFICATE----- 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 new file mode 100644 index 000000000..d9dc5ca0f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem @@ -0,0 +1,32 @@ +# 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 +LfEv5uFXX6gyd2WQwKCiGGf4UY4ZIl4lJVpSDsBV2orR4pOIf1s1+iSwvcRQkX46 +SVjoKWbDUc4VLo1uy8UvavQI+DMioYyy0K2MbRs7oG2rdKks8zisfT0ymKnrFTdV +eUjIrg0sStaMnf9VVkcEeYkfNY0vWqdnCV5wPfDBlnnxGMgqGdLSpzfyJ7qafFET ++q+gOvjsEqzn7DvlPkmk86hIIWXKi3aMA9swknL3rnagJL6GioWRpYUwKdRKmZxd +yr4I2JTTAgMBAAECggEBALpg9ZDUMCiOpc+mbNO/ZkP90M7u38Q0M+7HY8XHOPkt +l+XUkWueSMRLhSeLDzMlnwf1HyN8RZLaJkzP6XAL1VXEwuXAiIskaZ4Cg07Arp/W +8cHhf4CcMuUVuCtOZcC+ajD4Do5zn9vkm9yH0ap0o0LdoWa/a8WfU+luy0EHBsSW +6qqI+nqNFmISluVbfWt7t3zp273+8sir6YeHQu9G91/jzggv8rHmu4EHhi3cnU0K +vY6OPCGBL7nrg9Rv1LSFpH95TvlIM6/Cm0AjgW7m6XwWUTaI9p+GvKzrYUSLd9L/ +QxlmAwiu/sBTXLrsWyr8XEtj+lVGxQ6eFbf6E+lUm8ECgYEA+8Wgmhf3VsC3gvJz +w2jApEoOioD5iGOWGClGVURkfaBhFELr4XCTVMdBuCtxT7LYTMHTAlBqIbdWDjB4 +m/E417hLGogSDy7j0R0Mx75OOGEitxYUhe0VGDNoytgCNd2UnTMt42lp+9vAHZag +INhVDOnxRNdtNTf1yYkWUMEbh1sCgYEA7kZNJXPVYJtR78+km/Gcv64Umci7KUV+ +hYc7chR5xv3cXvXg5eojKa4G7CyMQTX7VnRa6CiQKdN73AbIAhS4Oy5UlCOKtmb8 +xnBiOAYwSpOfIeZhjq0RvEeZX0t6u7XsErBZ03rEPKXF2nNDo1x8byrlKPtlUzwJ +gb5yjmK/mekCgYEA1TWQAs5m4+2Bun+tbv7nnHkmhT4hktGays0xRYYMf6Jwc6MU +dC5MZg/zZI5Nf8uZhq7hDWWh6vmCA7QifxSxKWVlHIu8l2UDAhRSvVg4j2Aa8Obe +7GdQZNUsWhLBFHKXpuQvaRTc7q8yqxvicM4igDQg4EZ6sgW4vDm+TxapRF8CgYAz +n6mhPqpxRtWGxo8cdkmGwfmWpAXg2DykQ3teqQ8FTQUM0erLBWJe6mR3kONGUaLF +xWnYuMkbNsW0EwgMY17S+6O5gMXR5RhJChpNlxGpZrhoiNiEJ/0atMyG9/x8ZNrj +5a9ggU248hWe0bBK2YPgNgP2UBlQ4kYRBSkerkhi2QKBgF+tlpyqcU+0iY82qRS2 +wMf7oI2pWR8nX9LPAY/nnvwWvqwcAFJPMlSMTu8Ext6h7l9yu+7JGL6JWwsO57Lb +Gm/RxbuZ/kG/13+lSNmZiyHrhj6hZhkAMeFM34fpT4+DBXqSxZuvdrmwBc5B2jYg +F9Bv8gcmZlGhqONL23evr9Gu +-----END PRIVATE KEY----- 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 new file mode 100644 index 000000000..9e956e6b0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem @@ -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 + +-----BEGIN CERTIFICATE----- +MIIDRjCCAi6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH +ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAeFw0xOTAxMTEwODMz +MThaFw0yOTAxMDgwODMzMThaMC0xGjAYBgNVBAMMEWFuc2libGUudGxzLnRlc3Rz +MQ8wDQYDVQQKDAZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCoM+OQ3HCnCUAAz9KGGTwWB9hQbUfAZXm/stlb2/uOAp3rNwxAlCs/giymBHE6 +Iu6mrK006Vn+Z9ibqIrD2LuCOxcu25y8goqG62TgdP5sa9wR+597s0XssnwnaY8y +bJ3p2zWAJvMgqQ0iNW/ZynpWbO85K5SryUykF7FAeNU9ogGGlIwCPjHhPvnwjkqd +yDqaA1VaJKDUWIF9joI7sV4VLgGhQvzXRrHULsTeIF2m0+ebL0PTNEWHQ0dtgLYX +kW7YO4Y6+n3cjHNH4qTof8V30EK8pk8kTdJ/x6ubwf+klFCAyroOxNOaxUy299Oo +yD6qIPJPnGkPhrKtWnWIhNzJAgMBAAGjbTBrMAkGA1UdEwQCMAAwCwYDVR0PBAQD +AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMDwGA1UdEQQ1MDOCEWFuc2libGUudGxz +LnRlc3RzghNNYWNCb29rLVByby00LmxvY2Fsgglsb2NhbGhvc3QwDQYJKoZIhvcN +AQELBQADggEBAK214+VVXnGnsUlvd9Q6A2Ea6UGrr6b7xkmlnIaNd+6xoUsDsHob +srHYm7UC0uLi1KwSunI7AU5ZELVEUfAmJzh3O4d6C5sQyqKYPqd5harWOQ3BOD0I +plHpp7qMtsPDuJBtmE/bmvF85eto0H7pPz+cTTXRlOaVVeiHjMggFcXdy1MzGo9C +X/4wLQmsFeypTfe+ZGqvDh99VV+ffNMIsMh+opWEloaKiHmDKB6S9aC/MsVVM4RR +nHm/UKTOukaGE9QIPkSSaygv3sBkVnQ2SHMvvtnjPHVHlizNoq6+YTnuOvKpo4o5 +V7Bij+W7rkBQLsEfwv2IC+gzmRz2yxr2tXk= +-----END CERTIFICATE----- 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 new file mode 100644 index 000000000..3848ad7cf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem @@ -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 + +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqDPjkNxwpwlAAM/Shhk8FgfYUG1HwGV5v7LZW9v7jgKd6zcM +QJQrP4IspgRxOiLupqytNOlZ/mfYm6iKw9i7gjsXLtucvIKKhutk4HT+bGvcEfuf +e7NF7LJ8J2mPMmyd6ds1gCbzIKkNIjVv2cp6VmzvOSuUq8lMpBexQHjVPaIBhpSM +Aj4x4T758I5Kncg6mgNVWiSg1FiBfY6CO7FeFS4BoUL810ax1C7E3iBdptPnmy9D +0zRFh0NHbYC2F5Fu2DuGOvp93IxzR+Kk6H/Fd9BCvKZPJE3Sf8erm8H/pJRQgMq6 +DsTTmsVMtvfTqMg+qiDyT5xpD4ayrVp1iITcyQIDAQABAoIBAHPszzpXs4xr46Cr +mvyxB6hnX76OkpUXWwGz0fptcsI9K3mhRuB7PhNXNE53YVIgITreZ8G/0jZ0e+VM +E9dG2HS5JRE2ap/BmJfERJIuD+vJqrL6KMCondi0arz/E6I9GdjDK+xW69nmqRaa +nawM0KQgD//m+WAsLJYrfg5hORZwI2SHaahawnCp0QaMmz3bdDWKRacM3q0UFX46 +Ze6CaZkUn+e1rHsTMcZBvxQWIVzysFNXh150idIB/PxL5YfCQqTSAj1c/nxaxz6a +BvHFlpaYR3tvXXlexxfjglCwsGyckbvTyP1cBZqpv5oES+VKt2PrOve9Zyax+CYT +0uQf6cECgYEA09+46QHXLfWh6jiJYu9skC9UrLU5czfCNB6PrUtFcjPFMYjZDcw9 +inJmcuTPXmfplxc47YDfpwotU+szTJDF+R8kknnfw9zVr/sIwZ5wsFfUQl/56Svn +AIOVvHHvcvMX95XKGiuTsoCIJZNjJN3l3ztu/bRciuiVLyizglwIVrMCgYEAyzvK +PFlWilbp3GPJlnW7x1bUxe1ziLE/Um+ujZx96+fy34hJLFdNdNzpNUjoOf3IDTGq +6xl+vXcf12gimWMFcD3qNIGKHBDM9cIB2RDbb6YcqI8lOqopsmOyGmVLPkRpCoUK +72kacQwvw6M9xjmpiG3dN8lE881jDmZi+hyCnJMCgYEAoIQnQAhP8Jbeo2dP1q+T +bS0elnX532uH6xqYOW8EXwAPznZiEw0ANspzCWqGHHzXQMusKmtvhcq1CpXvWHt6 +MUHB4GMK/wVosxmZya5yq3bu7ZZu7JOBQCdwosMi6NB5AO7vnaIUFLFB9E3UWBLw +243YicdCMU8B7yeD0ChPfPcCgYA1dYHKBBn+g8Q6Y8lIGaoOUmnfsok8gJtOfPAm +ce6xmi7J29iboE9QmTeC+62Sa44u4ky6UNeE0QwAJnVLcb+hebfcneKNZWH0l1bT +GVsPcFuDfzvkxZP4R782sERtmaMj0EFDHpuE9xatWIhMVyigKX4SSZAorXML+6S3 +c75rnwKBgBR+WU934wS+DbwTLlUB2mJWqJMEbOH/CUwPC7+VN4h1h3/i455iAeiU +BizLS0SlD+MoSbC7URcZuquqGkmMlnJXoxF+NdxoWZK78tYNftryWoR87TloiVc/ +LhkxZxje4tgW/mTLqH3zKDoyyzDzG6Q6tAUN2ZTjJFEws7qF30Qe +-----END RSA PRIVATE KEY----- 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 new file mode 100644 index 000000000..b714ddbfb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem @@ -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 + +-----BEGIN CERTIFICATE----- +MIIDRjCCAi6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH +ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAeFw0xOTAxMTEwODMz +MTZaFw0yOTAxMDgwODMzMTZaMC0xGjAYBgNVBAMMEWFuc2libGUudGxzLnRlc3Rz +MQ8wDQYDVQQKDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDIwErHwAesRBfd9HiZkmB3VYh28c1QkE9I8nYyHJKX2ZBUhAzK+h80BkcTJJ94 +265qWyACH/wl54Xe/ofFUFrGa4vz0qz4UkL/KI0OGw28Y4qnKdorb9DumbiIPB+9 +I9TJT9vhtXTxBNlBTpv3ONHL8EzdV6ZmuvELU11H27oQ4xoUYhfXPXLMLK0sOnXZ +lt0BOMMd5fVpJVa8fvXiw3626a0aXCr4e/MWUsBFRnzrXfgoW+AjYoTjKKS2hLYo +8//MM05h7ROIXrNe990sf9C1G+fOThmOMszK9sjMhu2xHranRcz5aA0UTfyOjTs8 +9WexUYhC5VorYyRWtVZu2mDjAgMBAAGjbTBrMAkGA1UdEwQCMAAwCwYDVR0PBAQD +AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMDwGA1UdEQQ1MDOCEWFuc2libGUudGxz +LnRlc3RzghNNYWNCb29rLVByby00LmxvY2Fsgglsb2NhbGhvc3QwDQYJKoZIhvcN +AQELBQADggEBAFoPBeB6tQhFS1198sia5NDHDDrghDOIlE0QbaoA+MSKzsaIy8Mu +mNcM2ewYpT600XXTBxcqF6/vuKL9OEbvivtRYQu1YfkifN1jzREoWTieUkR5ytzt +8ATfFkgTWJmiRiOIb/fNgewvhd+aKxep0OGwDiSKKl1ab6F17Cp4iK8sDBWmnUb6 +0Wf7pfver1Gl0Gp8vRXGUuc8a7udA9a8mV70HJlLkMdMvR9U8Bqih0+iRaqNWXRZ +7Lc6v5LbzrW/ntilmgU6F0lwxPydg49MY4UrSXcjYLZs9T4iYHwTfLxFjFMIgGwn +peYMKRj18akP9i2mjj5O2mRu4K+ecuUSOGI= +-----END CERTIFICATE----- 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 new file mode 100644 index 000000000..ec0134993 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem @@ -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 + +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAyMBKx8AHrEQX3fR4mZJgd1WIdvHNUJBPSPJ2MhySl9mQVIQM +yvofNAZHEySfeNuualsgAh/8JeeF3v6HxVBaxmuL89Ks+FJC/yiNDhsNvGOKpyna +K2/Q7pm4iDwfvSPUyU/b4bV08QTZQU6b9zjRy/BM3VemZrrxC1NdR9u6EOMaFGIX +1z1yzCytLDp12ZbdATjDHeX1aSVWvH714sN+tumtGlwq+HvzFlLARUZ86134KFvg +I2KE4yiktoS2KPP/zDNOYe0TiF6zXvfdLH/QtRvnzk4ZjjLMyvbIzIbtsR62p0XM ++WgNFE38jo07PPVnsVGIQuVaK2MkVrVWbtpg4wIDAQABAoIBAHw3wA3pnNXTLJGC +fD1KfbZZjp9K76gyI10X6lsHow2i6dPiAah3LGecms4VkzfNdxcIW7303Kj3obZh ++ND277RnR6oPakgdXqdUCDP6OX2gemMFWqIWBkodhDmIOntmeHw4le4LwdiBD42B +frBy0B5JCsbLPYPDmPNRGh8krvVS+Eir4hb4tK95TPMSL0vEjvHYFbCxv7//Ri1p +3CROGp2CGX0WZ+Zs0crRNoIhRRM6kLAhROcqejtnEy6o7l5CWpCAL2vxlE9y8/kL +iRawSZRFZnz/zGnqpx0vswgvijkuPfcNGMSzdwaiDgQz8D0GkJ7s9VgzZJazNy+1 +ET/4YIECgYEA612rwP9Ar9qdYbmmMPaJzITnaIrNGfO2JvaQqZt+DG8sVgdxL7V5 +D6emcw406drKRZvFAxnW6ZW2bVpmit02osl0re2A/nOTXLNuo338Qkap/hG8YZrF +bw7w75pFa/rwlDtedjBnGHO2KbRXeU5Hn5wLoKjYgJoF6Ht+PPdL0IsCgYEA2lnC +pQEhM51iRMDqNdmVJyvsTNU1ikoO8HaXHq+LwOQETaKMnDwp4Bn14E815CTulAc/ +tsDTKSDk6umZ+IufG1a2v7CqgKVwkB4HkgxKFQs2gQdTFfoMi5eeHR+njuNtklp1 +9fWfKHsP/ddrg+iTVTRZBLWexgKK89IMHYalpAkCgYEAy0Q3a9NF81mTJ+3kOE8C +zO1OyLtuzGXsvxOb9c6C+owctyNwPeq05a89EgqH6hr5K0qOx9HOCCcyyJgVDQJl +CAuByB/gkmAQOTQBbhMFA9vxPanljknTDsnRjKwoHkw2712ig+Hjd3ufK79C+FGB +i7eBVzva1p2uUowshsxv3mcCgYAOFiRciMofjlO8o8V4W+Undcn02vxtQ4HbOYte +S2z0sMEmUQpJOghpkMMwCWwsn8VUf3M40w/MY3bhQNjSFA/br6hyjW8yhXnRkl5i +qbBN0z9c66AMlukgSFPHBTfGHB4Bhxx9Fa+C6Q2LDs6839BBevMTPrRTie509GQb +s4gUIQKBgAvE8wLcmozno0GLDnBdKRZP/C7tmVnAINuraITPUBTASwI+Qo8ILigQ +LRLaDqF84BEpjb8vdzkYFQqRQSZ8BI8NydfuKEFSBfL27sBvSGMYQJVm6bryUmPq +T3ayaeZ4Wb3FFDijgtM9dRKyf7p4hQPOqM44QrntAtb43b2Q5L7M +-----END RSA PRIVATE KEY----- diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_tls/tasks/main.yml new file mode 100644 index 000000000..ea4b9ecaa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/tasks/main.yml @@ -0,0 +1,30 @@ +--- +#################################################################### +# 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 + +# Generated certificate with: https://github.com/michaelklishin/tls-gen +# ~/tls-gen/basic# make PASSWORD=bunnies CN=ansible.tls.tests +# verify with: make info + +- name: ensure target directory is present + file: + path: /tls + state: directory + +- name: ensure TLS files are present + copy: + src: "{{ item }}" + dest: "/tls/{{ item }}" + loop: + - ca_certificate.pem + - ca_key.pem + - client_certificate.pem + - client_key.pem + - server_certificate.pem + - server_key.pem diff --git a/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/defaults/main.yml new file mode 100644 index 000000000..03b12c95c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/defaults/main.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 + +wf_tmp_dir: '{{ remote_tmp_dir }}/wildfly_tmp' +wf_homedir: '{{ wf_tmp_dir }}/wildfly' +wf_service_file_path: /etc/systemd/system/wildfly.service +wf_version: 16.0.0.Final +wf_user: wildfly +jboss_root: '{{ wf_homedir }}' +deploy_dir: '{{ jboss_root }}/standalone/deployments' +default_deploy_root: /var/lib/jbossas/standalone/deployments diff --git a/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/files/wildfly.conf b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/files/wildfly.conf new file mode 100644 index 000000000..684ce12a2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/files/wildfly.conf @@ -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 + +# The configuration you want to run +WILDFLY_CONFIG=standalone.xml + +# The mode you want to run +WILDFLY_MODE=standalone + +# The address to bind to +WILDFLY_BIND=0.0.0.0 diff --git a/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/handlers/main.yml new file mode 100644 index 000000000..1383b1575 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/handlers/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 + +- name: Stop wildfly (jboss) + systemd: + name: wildfly + state: stopped + ignore_errors: true + +- name: Remove files + file: + path: '{{ item }}' + state: absent + loop: + - '{{ wf_service_file_path }}' + - '{{ default_deploy_root }}' diff --git a/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/meta/main.yml new file mode 100644 index 000000000..2d29ebb67 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/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/setup_wildfly_server/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/tasks/main.yml new file mode 100644 index 000000000..26f5083b0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/tasks/main.yml @@ -0,0 +1,107 @@ +--- +#################################################################### +# 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: Skip unsupported platforms + meta: end_play + when: (ansible_distribution != 'CentOS') or + (ansible_distribution == 'CentOS' and ansible_distribution_major_version is not version('7', '>=')) + +- name: Install java + package: + name: java-1.8.0-openjdk-devel + +- name: Create wf_tmp_dir + file: + path: '{{ wf_tmp_dir }}' + state: directory + +- name: Download wildfly + get_url: + url: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/setup_wildfly_server/wildfly-{{ wf_version }}.tar.gz' + dest: '{{ wf_tmp_dir }}/wildfly-{{ wf_version }}.tar.gz' + +- name: Unarchive tar + unarchive: + src: '{{ wf_tmp_dir }}/wildfly-{{ wf_version }}.tar.gz' + dest: '{{ wf_tmp_dir }}' + remote_src: true + +- name: Remove tar + file: + path: '{{ wf_tmp_dir }}/wildfly-{{ wf_version }}.tar.gz' + state: absent + +- name: Create symlink + file: + src: '{{ wf_tmp_dir }}/wildfly-{{ wf_version }}' + dest: '{{ wf_tmp_dir }}/wildfly' + state: link + +- name: Create group for wildfly + group: + name: '{{ wf_user }}' + system: true + +- name: Create user for wildfly + user: + name: '{{ wf_user }}' + system: true + group: '{{ wf_user }}' + home: '{{ wf_homedir }}' + +- name: Set permissions + file: + path: '{{ remote_tmp_dir }}' + state: directory + owner: '{{ wf_user }}' + group: '{{ wf_user }}' + recurse: true + +- name: Create config file + copy: + src: wildfly.conf + dest: '{{ wf_homedir }}/wildfly.conf' + mode: "0644" + +- name: Create launcher + template: + src: launch.sh.j2 + dest: '{{ wf_homedir }}/bin/launch.sh' + mode: "0755" + +- name: Make scripts executable + shell: 'chmod +rx {{ wf_homedir }}/bin/*.sh' + +- name: Create service file + template: + src: wildfly.service.j2 + dest: '{{ wf_service_file_path }}' + mode: "0644" + +- name: Create directories for testing the default deploy_path + become: true + file: + path: '{{ default_deploy_root }}' + state: directory + recurse: true + owner: '{{ wf_user }}' + group: '{{ wf_user }}' + +- name: Create simlink for testing the default deploy_path + file: + state: link + src: '{{ deploy_dir }}' + dest: '{{ default_deploy_root }}/deployments' + +- name: Reload systemd and start wildfly + systemd: + daemon_reload: true + name: wildfly + state: started diff --git a/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/launch.sh.j2 b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/launch.sh.j2 new file mode 100644 index 000000000..7a80251a1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/launch.sh.j2 @@ -0,0 +1,14 @@ +#!/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 + +if [ "x$WILDFLY_HOME" = "x" ]; then + WILDFLY_HOME="{{ wf_homedir }}" +fi + +if [[ "$1" == "domain" ]]; then + $WILDFLY_HOME/bin/domain.sh -c "$2" -b "$3" +else + $WILDFLY_HOME/bin/standalone.sh -c "$2" -b "$3" +fi diff --git a/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/wildfly.service.j2 b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/wildfly.service.j2 new file mode 100644 index 000000000..ec1055132 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/setup_wildfly_server/templates/wildfly.service.j2 @@ -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 + +[Unit] +Description=The WildFly Application Server +After=syslog.target network.target +Before=httpd.service + +[Service] +Environment=LAUNCH_JBOSS_IN_BACKGROUND=1 +EnvironmentFile=-{{ wf_homedir }}/wildfly.conf +User=wildfly +LimitNOFILE=102642 +PIDFile=/var/run/wildfly/wildfly.pid +ExecStart={{ wf_homedir }}/bin/launch.sh $WILDFLY_MODE $WILDFLY_CONFIG $WILDFLY_BIND +StandardOutput=null + +[Install] +WantedBy=multi-user.target diff --git a/ansible_collections/community/general/tests/integration/targets/shutdown/aliases b/ansible_collections/community/general/tests/integration/targets/shutdown/aliases new file mode 100644 index 000000000..afda346c4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/shutdown/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/1 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 new file mode 100644 index 000000000..dadeb6269 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml @@ -0,0 +1,93 @@ +--- +#################################################################### +# 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 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: + delay: 100 + msg: "Custom Message" + register: shutdown_result + check_mode: true + +- name: Execute shutdown with minus delay + community.general.shutdown: + delay: -100 + register: shutdown_result_minus + check_mode: true + +- name: Verify Custom Message except Alpine, AIX + assert: + that: + - '"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'] + +- name: Verify shutdown command is present except Alpine, VMKernel + assert: + that: '"shutdown" in shutdown_result["shutdown_command"]' + when: ansible_os_family != 'Alpine' and ansible_system != 'VMKernel' + +- name: Verify shutdown command is present in Alpine + assert: + that: '"poweroff" in shutdown_result["shutdown_command"]' + when: ansible_os_family == 'Alpine' + +- name: Verify shutdown command is present in VMKernel + assert: + that: '"halt" in shutdown_result["shutdown_command"]' + when: ansible_system == 'VMKernel' + +- name: Verify shutdown delay is present in minutes in Linux + 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' + +- name: Verify shutdown delay is present in minutes in Void, MacOSX, OpenBSD + assert: + that: + - '"-h +1" in shutdown_result["shutdown_command"]' + - '"-h +0" in shutdown_result_minus["shutdown_command"]' + when: ansible_system in ['Void', 'Darwin', 'OpenBSD'] + +- 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"]' + when: ansible_system == 'FreeBSD' + +- name: Verify shutdown delay is present in seconds in Solaris, SunOS + assert: + that: + - '"-g 100" in shutdown_result["shutdown_command"]' + - '"-g 0" in shutdown_result_minus["shutdown_command"]' + when: ansible_system in ['Solaris', 'SunOS'] + +- name: Verify shutdown delay is present in seconds, VMKernel + assert: + that: + - '"-d 100" in shutdown_result["shutdown_command"]' + - '"-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 + apt: + name: systemd-sysv + state: absent + when: systemd_sysv_install is changed diff --git a/ansible_collections/community/general/tests/integration/targets/snap/aliases b/ansible_collections/community/general/tests/integration/targets/snap/aliases new file mode 100644 index 000000000..b209bbc01 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap/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/1 +azp/posix/vm +skip/aix +skip/alpine +skip/fedora +skip/freebsd +skip/osx +skip/macos +skip/docker 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 new file mode 100644 index 000000000..f36427f71 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap/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_snap 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 new file mode 100644 index 000000000..0f24e69f3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml @@ -0,0 +1,243 @@ +--- +#################################################################### +# 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: Has-snap block + 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 diff --git a/ansible_collections/community/general/tests/integration/targets/snap_alias/aliases b/ansible_collections/community/general/tests/integration/targets/snap_alias/aliases new file mode 100644 index 000000000..b209bbc01 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap_alias/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/1 +azp/posix/vm +skip/aix +skip/alpine +skip/fedora +skip/freebsd +skip/osx +skip/macos +skip/docker diff --git a/ansible_collections/community/general/tests/integration/targets/snap_alias/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/snap_alias/meta/main.yml new file mode 100644 index 000000000..f36427f71 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap_alias/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_snap diff --git a/ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/main.yml new file mode 100644 index 000000000..1934eeb9f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/main.yml @@ -0,0 +1,13 @@ +--- +#################################################################### +# 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: Test + include_tasks: test.yml + when: has_snap diff --git a/ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/test.yml new file mode 100644 index 000000000..50e6e33b4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/snap_alias/tasks/test.yml @@ -0,0 +1,159 @@ +--- +# 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 snap 'hello-world' is not installed + community.general.snap: + name: hello-world + state: absent + +- name: Ensure snap 'hello-world' is installed fresh + community.general.snap: + name: hello-world + +################################################################################ + +- name: Create snap alias (check mode) + community.general.snap_alias: + name: hello-world + alias: hw + check_mode: true + register: alias_single_0 + +- name: Create snap alias + community.general.snap_alias: + name: hello-world + alias: hw + register: alias_single_1 + +- name: Create snap alias (check mode idempotent) + community.general.snap_alias: + name: hello-world + alias: hw + check_mode: true + register: alias_single_2 + +- name: Create snap alias (idempotent) + community.general.snap_alias: + name: hello-world + alias: hw + register: alias_single_3 + +- name: assert single alias + assert: + that: + - alias_single_0 is changed + - alias_single_1 is changed + - alias_single_2 is not changed + - alias_single_3 is not changed + - 'alias_single_1.snap_aliases["hello-world"] == ["hw"]' + - 'alias_single_3.snap_aliases["hello-world"] == ["hw"]' + +- name: Create multiple aliases (check mode) + community.general.snap_alias: + name: hello-world + aliases: [hw, hw2, hw3] + check_mode: true + register: alias_multi_0 + +- name: Create multiple aliases + community.general.snap_alias: + name: hello-world + aliases: [hw, hw2, hw3] + register: alias_multi_1 + +- name: Create multiple aliases (check mode idempotent) + community.general.snap_alias: + name: hello-world + aliases: [hw, hw2, hw3] + check_mode: true + register: alias_multi_2 + +- name: Create multiple aliases (idempotent) + community.general.snap_alias: + name: hello-world + aliases: [hw, hw2, hw3] + register: alias_multi_3 + +- name: assert multi alias + assert: + that: + - alias_multi_0 is changed + - alias_multi_1 is changed + - alias_multi_2 is not changed + - alias_multi_3 is not changed + - 'alias_multi_1.snap_aliases["hello-world"] == ["hw", "hw2", "hw3"]' + - 'alias_multi_3.snap_aliases["hello-world"] == ["hw", "hw2", "hw3"]' + +- name: Remove one specific alias (check mode) + community.general.snap_alias: + alias: hw + state: absent + check_mode: true + register: alias_remove_0 + +- name: Remove one specific alias + community.general.snap_alias: + alias: hw + state: absent + register: alias_remove_1 + +- name: Remove one specific alias (check mode idempotent) + community.general.snap_alias: + alias: hw + state: absent + check_mode: true + register: alias_remove_2 + +- name: Remove one specific alias (idempotent) + community.general.snap_alias: + alias: hw + state: absent + register: alias_remove_3 + +- name: assert remove alias + assert: + that: + - alias_remove_0 is changed + - alias_remove_1 is changed + - alias_remove_2 is not changed + - alias_remove_3 is not changed + - 'alias_remove_1.snap_aliases["hello-world"] == ["hw2", "hw3"]' + - 'alias_remove_3.snap_aliases["hello-world"] == ["hw2", "hw3"]' + +- name: Remove all aliases for snap (check mode) + community.general.snap_alias: + name: hello-world + state: absent + check_mode: true + register: alias_remove_all_0 + +- name: Remove all aliases for snap + community.general.snap_alias: + name: hello-world + state: absent + register: alias_remove_all_1 + +- name: Remove all aliases for snap (check mode idempotent) + community.general.snap_alias: + name: hello-world + state: absent + check_mode: true + register: alias_remove_all_2 + +- name: Remove all aliases for snap (idempotent) + community.general.snap_alias: + name: hello-world + state: absent + register: alias_remove_all_3 + +- name: assert remove_all alias + assert: + that: + - alias_remove_all_0 is changed + - alias_remove_all_1 is changed + - alias_remove_all_2 is not changed + - alias_remove_all_3 is not changed + - 'alias_remove_all_1.snap_aliases["hello-world"] == []' + - 'alias_remove_all_3.snap_aliases["hello-world"] == []' diff --git a/ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/aliases b/ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/aliases new file mode 100644 index 000000000..bd1f02444 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/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/spectrum_model_attrs/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/tasks/main.yml new file mode 100644 index 000000000..42e53d7d7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/spectrum_model_attrs/tasks/main.yml @@ -0,0 +1,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: "Verify required variables: model_name, model_type, oneclick_username, oneclick_password, oneclick_url" + fail: + msg: "One or more of the following variables are not set: model_name, model_type, oneclick_username, oneclick_password, oneclick_url" + when: > + model_name is not defined + or model_type is not defined + or oneclick_username is not defined + or oneclick_password is not defined + or oneclick_url is not defined + +- block: + - name: "001: Enforce maintenance mode for {{ model_name }} with a note about why [check_mode test]" + spectrum_model_attrs: &mm_enabled_args + url: "{{ oneclick_url }}" + username: "{{ oneclick_username }}" + password: "{{ oneclick_password }}" + name: "{{ model_name }}" + type: "{{ model_type }}" + validate_certs: false + attributes: + - name: "isManaged" + value: "false" + - name: "Notes" + value: "{{ note_mm_enabled }}" + check_mode: true + register: mm_enabled_check_mode + + - name: "001: assert that changes were made" + assert: + that: + - mm_enabled_check_mode is changed + + - name: "001: assert that changed_attrs is properly set" + assert: + that: + - mm_enabled_check_mode.changed_attrs.Notes == note_mm_enabled + - mm_enabled_check_mode.changed_attrs.isManaged == "false" + + - name: "002: Enforce maintenance mode for {{ model_name }} with a note about why" + spectrum_model_attrs: + <<: *mm_enabled_args + register: mm_enabled + check_mode: false + + - name: "002: assert that changes were made" + assert: + that: + - mm_enabled is changed + + - name: "002: assert that changed_attrs is properly set" + assert: + that: + - mm_enabled.changed_attrs.Notes == note_mm_enabled + - mm_enabled.changed_attrs.isManaged == "false" + + - name: "003: Enforce maintenance mode for {{ model_name }} with a note about why [idempontence test]" + spectrum_model_attrs: + <<: *mm_enabled_args + register: mm_enabled_idp + check_mode: false + + - name: "003: assert that changes were not made" + assert: + that: + - mm_enabled_idp is not changed + + - name: "003: assert that changed_attrs is not set" + assert: + that: + - mm_enabled_idp.changed_attrs == {} + + vars: + note_mm_enabled: "MM set via CO #1234 by OJ Simpson" diff --git a/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases b/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases new file mode 100644 index 000000000..6011128da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases @@ -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 + +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/files/fake_id_rsa b/ansible_collections/community/general/tests/integration/targets/ssh_config/files/fake_id_rsa new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/integration/targets/ssh_config/files/ssh_config_test b/ansible_collections/community/general/tests/integration/targets/ssh_config/files/ssh_config_test new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/integration/targets/ssh_config/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/ssh_config/meta/main.yml new file mode 100644 index 000000000..4cdaaefba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ssh_config/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_remote_constraints + - setup_remote_tmp_dir diff --git a/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/main.yml new file mode 100644 index 000000000..c8b96d0c0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/main.yml @@ -0,0 +1,245 @@ +# Test code for ssh_config module +# Copyright (c) 2021, Abhijeet Kasurde (@Akasurde) +# 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: + - 'paramiko<3.0.0' + state: present + extra_args: "-c {{ remote_constraints }}" + +- set_fact: + output_test_dir: '{{ remote_tmp_dir }}/test_ssh_config' + +- set_fact: + ssh_config_test: '{{ output_test_dir }}/ssh_config_test' + ssh_private_key: '{{ output_test_dir }}/fake_id_rsa' + +- name: create a temporary directory + file: + path: "{{ output_test_dir }}" + state: directory + +- name: Copy sample config file + copy: + src: 'files/ssh_config_test' + dest: '{{ ssh_config_test }}' + +- name: Copy sample private key file + copy: + src: 'files/fake_id_rsa' + dest: '{{ ssh_private_key }}' + +- name: Fail for required argument + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + ignore_errors: true + register: host_required + +- name: Check if ssh_config fails for required parameter host + assert: + that: + - not host_required.changed + +- name: Add a host in check mode + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + identity_file: '{{ ssh_private_key }}' + port: '2223' + state: present + register: host_add + check_mode: true + +- name: Check if changes are made in check mode + assert: + that: + - host_add.changed + - "'example.com' in host_add.hosts_added" + - host_add.hosts_changed is defined + - host_add.hosts_removed is defined + +- name: Add a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + identity_file: '{{ ssh_private_key }}' + port: '2223' + state: present + register: host_add + +- name: Check if changes are made + assert: + that: + - host_add.changed + - "'example.com' in host_add.hosts_added" + - host_add.hosts_changed is defined + - host_add.hosts_removed is defined + +- name: Add same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + identity_file: '{{ ssh_private_key }}' + port: '2223' + state: present + register: host_add_again + +- name: Check for idempotency + assert: + that: + - not host_add_again.changed + - host_add.hosts_changed is defined + - host_add.hosts_removed is defined + - host_add.hosts_added is defined + +- name: Update host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + identity_file: '{{ ssh_private_key }}' + port: '2224' + state: present + register: host_update + +- name: Check for update operation + assert: + that: + - host_update.changed + - host_update.hosts_changed is defined + - "'example.com' in host_update.hosts_changed" + - host_update.hosts_removed is defined + - host_update.hosts_added is defined + - host_update.hosts_change_diff is defined + +- name: Update host again + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + identity_file: '{{ ssh_private_key }}' + port: '2224' + state: present + register: host_update + +- name: Check update operation for idempotency + assert: + that: + - not host_update.changed + - host_update.hosts_changed is defined + - host_update.hosts_removed is defined + - host_update.hosts_added is defined + - host_update.hosts_change_diff is defined + +- name: Delete a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + state: absent + register: host_delete + +- name: Check if changes are made + assert: + that: + - host_delete.changed + - "'example.com' in host_delete.hosts_removed" + - host_delete.hosts_changed is defined + - host_delete.hosts_added is defined + +- name: Delete same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + state: absent + register: host_delete_again + +- name: Check for idempotency + assert: + that: + - not host_delete_again.changed + - host_delete_again.hosts_changed is defined + - host_delete_again.hosts_removed is defined + - host_delete_again.hosts_added is defined + +- name: Check if user and ssh_config_file are mutually exclusive + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + user: root + host: "example.com" + hostname: github.com + identity_file: '{{ ssh_private_key }}' + port: '2223' + state: present + register: mut_ex + ignore_errors: true + +- name: Check mutual exclusive test - user and ssh_config_file + assert: + that: + - not mut_ex.changed + - "'parameters are mutually exclusive' in mut_ex.msg" + +- name: Check if proxycommand and proxyjump are mutually exclusive + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "example.com" + hostname: github.com + proxycommand: "ssh jumphost.example.com -W %h:%p" + proxyjump: "jumphost.example.com" + identity_file: '{{ ssh_private_key }}' + port: '2224' + state: present + register: proxy_mut_ex + ignore_errors: true + +- name: Check mutual exclusive test - proxycommand and proxyjump + assert: + that: + - not proxy_mut_ex.changed + - "'parameters are mutually exclusive' in proxy_mut_ex.msg" + +- name: Add a full name host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "full_name" + hostname: full_name.com + identity_file: '{{ ssh_private_key }}' + port: '2223' + state: present + register: full_name + +- name: Check if changes are made + assert: + that: + - full_name is changed + - full_name.hosts_added == ["full_name"] + - full_name.hosts_changed == [] + - full_name.hosts_removed == [] + +- name: Add a host with name which is contained in full name host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "full" + hostname: full.com + identity_file: '{{ ssh_private_key }}' + port: '2223' + state: present + register: short_name + +- name: Check that short name host is added and full name host is not updated + assert: + that: + - short_name is changed + - short_name.hosts_added == ["full"] + - short_name.hosts_changed == [] + - short_name.hosts_removed == [] + +- name: Include integration tests for additional options (e.g. proxycommand, proxyjump) + include_tasks: 'options.yml' 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 new file mode 100644 index 000000000..406de6831 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml @@ -0,0 +1,422 @@ +--- +# 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 + +# Reset ssh_config before testing options +- name: Copy sample config file + copy: + src: 'files/ssh_config_test' + dest: '{{ ssh_config_test }}' + +- name: Options - Add in check mode + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxycommand: "ssh jumphost.example.com -W %h:%p" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add + check_mode: true + +- name: Options - Check if changes are made in check mode + assert: + that: + - options_add.changed + - "'options.example.com' in options_add.hosts_added" + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Options - Verify that nothign was added to {{ ssh_config_test }} during change mode" + assert: + that: + - "'options.example.com' not in slurp_ssh_config['content'] | b64decode" + +- name: Options - Add a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxycommand: "ssh jumphost.example.com -W %h:%p" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add + +- name: Options - Check if changes are made + assert: + that: + - options_add.changed + - "'options.example.com' in options_add.hosts_added" + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + +- name: Options - Add same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxycommand: "ssh jumphost.example.com -W %h:%p" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add_again + +- name: Options - Check for idempotency + assert: + that: + - not options_add_again.changed + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + - options_add.hosts_added is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} contains added options" + assert: + that: + - "'proxycommand ssh jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent yes' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-rsa' in slurp_ssh_config['content'] | b64decode" + +- name: Options - Update host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxycommand: "ssh new-jumphost.example.com -W %h:%p" + forward_agent: false + host_key_algorithms: "+ssh-ed25519" + state: present + register: options_update + +- name: Options - Check for update operation + assert: + that: + - options_update.changed + - options_update.hosts_changed is defined + - "'options.example.com' in options_update.hosts_changed" + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: Options - Update host again + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxycommand: "ssh new-jumphost.example.com -W %h:%p" + forward_agent: false + host_key_algorithms: "+ssh-ed25519" + state: present + register: options_update + +- name: Options - Check update operation for idempotency + assert: + that: + - not options_update.changed + - options_update.hosts_changed is defined + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} contains changed options" + assert: + that: + - "'proxycommand ssh new-jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' 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: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: present + register: options_no_update + +- name: Options - Check that no update took place + assert: + that: + - not options_update.changed + - options_update.hosts_changed is defined + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} wasn't changed" + assert: + that: + - "'proxycommand ssh new-jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode" + +- name: Debug + debug: + msg: "{{ slurp_ssh_config['content'] | b64decode }}" + +- name: Options - Delete a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: absent + register: options_delete + +- name: Options - Check if host was removed + assert: + that: + - options_delete.changed + - "'options.example.com' in options_delete.hosts_removed" + - options_delete.hosts_changed is defined + - options_delete.hosts_added is defined + +- name: Options - Delete same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: absent + register: options_delete_again + +- name: Options - Check delete operation for idempotency + assert: + that: + - not options_delete_again.changed + - options_delete_again.hosts_changed is defined + - options_delete_again.hosts_removed is defined + - options_delete_again.hosts_added is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} does not contains deleted options" + assert: + 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" + - "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode" + +# Proxycommand and ProxyJump are mutually exclusive. +# Reset ssh_config before testing options with proxyjump + +- name: Copy sample config file + copy: + src: 'files/ssh_config_test' + dest: '{{ ssh_config_test }}' + +- name: Options - Add in check mode + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "jumphost.example.com" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add + check_mode: true + +- name: Options - Check if changes are made in check mode + assert: + that: + - options_add.changed + - "'options.example.com' in options_add.hosts_added" + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Options - Verify that nothign was added to {{ ssh_config_test }} during change mode" + assert: + that: + - "'options.example.com' not in slurp_ssh_config['content'] | b64decode" + +- name: Options - Add a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "jumphost.example.com" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add + +- name: Options - Check if changes are made + assert: + that: + - options_add.changed + - "'options.example.com' in options_add.hosts_added" + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + +- name: Options - Add same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "jumphost.example.com" + forward_agent: true + host_key_algorithms: "+ssh-rsa" + state: present + register: options_add_again + +- name: Options - Check for idempotency + assert: + that: + - not options_add_again.changed + - options_add.hosts_changed is defined + - options_add.hosts_removed is defined + - options_add.hosts_added is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} contains added options" + assert: + that: + - "'proxyjump jumphost.example.com' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent yes' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-rsa' in slurp_ssh_config['content'] | b64decode" + +- name: Options - Update host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "new-jumphost.example.com" + forward_agent: false + host_key_algorithms: "+ssh-ed25519" + state: present + register: options_update + +- name: Options - Check for update operation + assert: + that: + - options_update.changed + - options_update.hosts_changed is defined + - "'options.example.com' in options_update.hosts_changed" + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: Options - Update host again + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + proxyjump: "new-jumphost.example.com" + forward_agent: false + host_key_algorithms: "+ssh-ed25519" + state: present + register: options_update + +- name: Options - Check update operation for idempotency + assert: + that: + - not options_update.changed + - options_update.hosts_changed is defined + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} contains changed options" + assert: + that: + - "'proxyjump new-jumphost.example.com' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' 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: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: present + register: options_no_update + +- name: Options - Check that no update took place + assert: + that: + - not options_update.changed + - options_update.hosts_changed is defined + - options_update.hosts_removed is defined + - options_update.hosts_added is defined + - options_update.hosts_change_diff is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} wasn't changed" + assert: + that: + - "'proxyjump new-jumphost.example.com' in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode" + +- name: Debug + debug: + msg: "{{ slurp_ssh_config['content'] | b64decode }}" + +- name: Options - Delete a host + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: absent + register: options_delete + +- name: Options - Check if host was removed + assert: + that: + - options_delete.changed + - "'options.example.com' in options_delete.hosts_removed" + - options_delete.hosts_changed is defined + - options_delete.hosts_added is defined + +- name: Options - Delete same host again for idempotency + community.general.ssh_config: + ssh_config_file: "{{ ssh_config_test }}" + host: "options.example.com" + state: absent + register: options_delete_again + +- name: Options - Check delete operation for idempotency + assert: + that: + - not options_delete_again.changed + - options_delete_again.hosts_changed is defined + - options_delete_again.hosts_removed is defined + - options_delete_again.hosts_added is defined + +- name: "Options - Get content of {{ ssh_config_test }}" + slurp: + src: "{{ ssh_config_test }}" + register: slurp_ssh_config + +- name: "Verify that {{ ssh_config_test }} does not contains deleted options" + assert: + that: + - "'proxyjump new-jumphost.example.com' not in slurp_ssh_config['content'] | b64decode" + - "'forwardagent no' not in slurp_ssh_config['content'] | b64decode" + - "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode" diff --git a/ansible_collections/community/general/tests/integration/targets/sudoers/aliases b/ansible_collections/community/general/tests/integration/targets/sudoers/aliases new file mode 100644 index 000000000..12d1d6617 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sudoers/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/sudoers/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml new file mode 100644 index 000000000..dd62025d5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml @@ -0,0 +1,279 @@ +--- +# 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 + +# Initialise environment + +- name: Register variables + set_fact: + sudoers_path: /etc/sudoers.d + alt_sudoers_path: /etc/sudoers_alt + +- name: Install sudo package + ansible.builtin.package: + name: sudo + when: ansible_os_family != 'Darwin' + +- name: Ensure sudoers directory exists + ansible.builtin.file: + path: "{{ sudoers_path }}" + state: directory + recurse: true + +- name: Ensure alternative sudoers directory exists + ansible.builtin.file: + path: "{{ alt_sudoers_path }}" + state: directory + recurse: true + + +# Run module and collect data + +- name: Create first rule + community.general.sudoers: + name: my-sudo-rule-1 + state: present + user: alice + commands: /usr/local/bin/command + register: rule_1 + +- name: Stat my-sudo-rule-1 file + ansible.builtin.stat: + path: "{{ sudoers_path }}/my-sudo-rule-1" + register: rule_1_stat + +- name: Grab contents of my-sudo-rule-1 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-1" + register: rule_1_contents + +- name: Create first rule again + community.general.sudoers: + name: my-sudo-rule-1 + state: present + user: alice + commands: /usr/local/bin/command + register: rule_1_again + + +- name: Create second rule with two commands + community.general.sudoers: + name: my-sudo-rule-2 + state: present + user: alice + commands: + - /usr/local/bin/command1 + - /usr/local/bin/command2 + register: rule_2 + +- name: Grab contents of my-sudo-rule-2 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-2" + register: rule_2_contents + + +- name: Create rule requiring a password + community.general.sudoers: + name: my-sudo-rule-3 + state: present + user: alice + commands: /usr/local/bin/command + nopassword: false + register: rule_3 + +- name: Grab contents of my-sudo-rule-3 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-3" + register: rule_3_contents + + +- name: Create rule using a group + community.general.sudoers: + name: my-sudo-rule-4 + state: present + group: students + commands: /usr/local/bin/command + register: rule_4 + +- name: Grab contents of my-sudo-rule-4 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-4" + register: rule_4_contents + + +- name: Create rule in a alternative directory + community.general.sudoers: + name: my-sudo-rule-5 + state: present + user: alice + commands: /usr/local/bin/command + sudoers_path: "{{ alt_sudoers_path }}" + register: rule_5 + +- name: Grab contents of my-sudo-rule-5 (in alternative directory) + ansible.builtin.slurp: + src: "{{ alt_sudoers_path }}/my-sudo-rule-5" + register: rule_5_contents + +- name: Create rule to runas another user + community.general.sudoers: + name: my-sudo-rule-6 + state: present + user: alice + commands: /usr/local/bin/command + runas: bob + sudoers_path: "{{ sudoers_path }}" + register: rule_6 + +- name: Grab contents of my-sudo-rule-6 (in alternative directory) + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-6" + register: rule_6_contents + +- name: Create rule to allow user to sudo just on host-1 + community.general.sudoers: + name: my-sudo-rule-7 + state: present + user: alice + host: host-1 + commands: /usr/local/bin/command + register: rule_7 + +- name: Grab contents of my-sudo-rule-7 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-7" + register: rule_7_contents + +- name: Create rule with setenv parameters + community.general.sudoers: + name: my-sudo-rule-8 + state: present + user: alice + commands: /usr/local/bin/command + setenv: true + register: rule_8 + +- name: Grab contents of my-sudo-rule-8 + ansible.builtin.slurp: + src: "{{ sudoers_path }}/my-sudo-rule-8" + register: rule_8_contents + +- name: Revoke rule 1 + community.general.sudoers: + name: my-sudo-rule-1 + state: absent + register: revoke_rule_1 + +- name: Stat rule 1 + ansible.builtin.stat: + path: "{{ sudoers_path }}/my-sudo-rule-1" + register: revoke_rule_1_stat + + +# Validation testing + +- name: Attempt command without full path to executable + community.general.sudoers: + name: edge-case-1 + state: present + user: alice + commands: systemctl + ignore_errors: true + register: edge_case_1 + + +- name: Attempt command without full path to executable, but disabling validation + community.general.sudoers: + name: edge-case-2 + state: present + user: alice + commands: systemctl + validation: absent + sudoers_path: "{{ alt_sudoers_path }}" + register: edge_case_2 + +- name: find visudo + command: + cmd: which visudo + register: which_visudo + when: ansible_os_family != 'Darwin' + +- name: Prevent visudo being executed + file: + path: "{{ which_visudo.stdout }}" + mode: '-x' + when: ansible_os_family != 'Darwin' + +- name: Attempt command without full path to executable, but enforcing validation with no visudo present + community.general.sudoers: + name: edge-case-3 + state: present + user: alice + commands: systemctl + validation: required + ignore_errors: true + when: ansible_os_family != 'Darwin' + register: edge_case_3 + +- name: Revoke non-existing rule + community.general.sudoers: + name: non-existing-rule + state: absent + register: revoke_non_existing_rule + +- name: Stat non-existing rule + ansible.builtin.stat: + path: "{{ sudoers_path }}/non-existing-rule" + register: revoke_non_existing_rule_stat + + +# Run assertions + +- name: Check rule 1 file stat + ansible.builtin.assert: + that: + - rule_1_stat.stat.exists + - rule_1_stat.stat.isreg + - rule_1_stat.stat.mode == '0440' + +- name: Check changed status + ansible.builtin.assert: + that: + - rule_1 is changed + - rule_1_again is not changed + - rule_5 is changed + - revoke_rule_1 is changed + - revoke_non_existing_rule is not changed + +- name: Check contents + ansible.builtin.assert: + that: + - "rule_1_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command\n'" + - "rule_2_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command1, /usr/local/bin/command2\n'" + - "rule_3_contents['content'] | b64decode == 'alice ALL= /usr/local/bin/command\n'" + - "rule_4_contents['content'] | b64decode == '%students ALL=NOPASSWD: /usr/local/bin/command\n'" + - "rule_5_contents['content'] | b64decode == 'alice ALL=NOPASSWD: /usr/local/bin/command\n'" + - "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'" + +- name: Check revocation stat + ansible.builtin.assert: + that: + - not revoke_rule_1_stat.stat.exists + - not revoke_non_existing_rule_stat.stat.exists + +- name: Check edge case responses + ansible.builtin.assert: + that: + - edge_case_1 is failed + - "'Failed to validate sudoers rule' in edge_case_1.msg" + - edge_case_2 is not failed + +- name: Check missing validation edge case + ansible.builtin.assert: + that: + - edge_case_3 is failed + - "'Failed to find required executable' in edge_case_3.msg" + when: ansible_os_family != 'Darwin' diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/aliases b/ansible_collections/community/general/tests/integration/targets/supervisorctl/aliases new file mode 100644 index 000000000..58524f1fb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/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/python3 +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/files/sendProcessStdin.py b/ansible_collections/community/general/tests/integration/targets/supervisorctl/files/sendProcessStdin.py new file mode 100644 index 000000000..8635b0749 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/files/sendProcessStdin.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# 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 + +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys + +proc = sys.argv[1] +value = sys.argv[2] +username = sys.argv[3] +password = sys.argv[4] + +if sys.version_info[0] == 2: + from xmlrpclib import ServerProxy + from urllib import quote +else: + from xmlrpc.client import ServerProxy + from urllib.parse import quote + +if username: + url = 'http://%s:%s@127.0.0.1:9001/RPC2' % (quote(username, safe=''), quote(password, safe='')) +else: + url = 'http://127.0.0.1:9001/RPC2' + +server = ServerProxy(url, verbose=True) +server.supervisor.sendProcessStdin(proc, 'import sys; print(%s); sys.stdout.flush();\n' % value) diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/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/supervisorctl/tasks/install_Darwin.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Darwin.yml new file mode 100644 index 000000000..b1d3bd779 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Darwin.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: install supervisord + pip: + name: supervisor<4.0.0 # supervisor version 4.0.0 fails tests + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_FreeBSD.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_FreeBSD.yml new file mode 100644 index 000000000..b1d3bd779 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_FreeBSD.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: install supervisord + pip: + name: supervisor<4.0.0 # supervisor version 4.0.0 fails tests + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Linux.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Linux.yml new file mode 100644 index 000000000..2f70b284c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Linux.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: install supervisor + package: + name: supervisor + state: present + +- name: disable supervisord system service + service: + name: '{{ supervisor_service_name }}' + state: stopped + enabled: false diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_RedHat.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_RedHat.yml new file mode 100644 index 000000000..b1d3bd779 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_RedHat.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: install supervisord + pip: + name: supervisor<4.0.0 # supervisor version 4.0.0 fails tests + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Suse.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Suse.yml new file mode 100644 index 000000000..b1d3bd779 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_Suse.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: install supervisord + pip: + name: supervisor<4.0.0 # supervisor version 4.0.0 fails tests + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_pip.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_pip.yml new file mode 100644 index 000000000..b1d3bd779 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/install_pip.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: install supervisord + pip: + name: supervisor<4.0.0 # supervisor version 4.0.0 fails tests + state: present diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/main.yml new file mode 100644 index 000000000..6f8c7968c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/main.yml @@ -0,0 +1,57 @@ +--- +#################################################################### +# 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: + - tempfile: + state: directory + suffix: supervisorctl-tests + register: supervisord_sock_path + + - command: 'echo {{ remote_tmp_dir }}' + register: echo + - set_fact: + remote_dir: '{{ echo.stdout }}' + + - include_vars: '{{ item }}' + with_first_found: + - files: + - '{{ ansible_distribution }}.yml' + - '{{ ansible_os_family }}.yml' + - 'defaults.yml' + + - include_tasks: '{{ item }}' + with_first_found: + - files: + - 'install_{{ ansible_distribution }}.yml' # CentOS + - 'install_{{ ansible_os_family }}.yml' # RedHat + - 'install_{{ ansible_system }}.yml' # Linux + + - include_tasks: test.yml + with_items: + - { username: '', password: '' } + - { username: 'testétest', password: 'passéword' } # non-ASCII credentials + loop_control: + loop_var: credentials + + # setuptools is too old on RHEL/CentOS 6 (https://github.com/Supervisor/meld3/issues/23) + when: ansible_os_family != 'RedHat' or ansible_distribution_major_version|int > 6 + + always: + - include_tasks: '{{ item }}' + when: ansible_os_family != 'RedHat' or ansible_distribution_major_version|int > 6 + with_first_found: + - files: + - 'uninstall_{{ ansible_distribution }}.yml' # CentOS + - 'uninstall_{{ ansible_os_family }}.yml' # RedHat + - 'uninstall_{{ ansible_system }}.yml' # Linux + + - file: + path: '{{ supervisord_sock_path.path }}' + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/start_supervisord.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/start_supervisord.yml new file mode 100644 index 000000000..906d7aca4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/start_supervisord.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: start supervisord + command: 'supervisord -c {{ remote_dir }}/supervisord.conf' + +- name: wait_for supervisord + ansible.builtin.wait_for: + port: 9001 + host: 127.0.0.1 + timeout: 15 + state: started diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/stop_supervisord.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/stop_supervisord.yml new file mode 100644 index 000000000..52e064d15 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/stop_supervisord.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: stop supervisord + command: "supervisorctl -c {{ remote_dir }}/supervisord.conf {% if credentials.username %}-u {{ credentials.username }} -p {{ credentials.password }}{% endif %} shutdown" diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test.yml new file mode 100644 index 000000000..5d1a867ed --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test.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 + +- name: generate supervisor configuration + template: + src: supervisord.conf + dest: '{{ remote_dir }}/supervisord.conf' + +- block: + - import_tasks: start_supervisord.yml + + - import_tasks: test_start.yml + - import_tasks: test_stop.yml + always: + - import_tasks: stop_supervisord.yml diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_start.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_start.yml new file mode 100644 index 000000000..b814486cd --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_start.yml @@ -0,0 +1,140 @@ +--- +# 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: start py1 service (without auth) + supervisorctl: + name: 'pys:py1' + state: started + config: '{{ remote_dir }}/supervisord.conf' + register: result + when: credentials.username == '' + +- name: start py1 service (with auth) + supervisorctl: + name: 'pys:py1' + state: started + server_url: http://127.0.0.1:9001 + username: '{{ credentials.username }}' + password: '{{ credentials.password }}' + register: result_with_auth + when: credentials.username != '' + +- command: "supervisorctl -c {{ remote_dir }}/supervisord.conf {% if credentials.username %}-u {{ credentials.username }} -p {{ credentials.password }}{% endif %} status" + +- name: check that service is started + assert: + that: + - (result is success and result_with_auth is skip) or (result is skip and result_with_auth is changed) + - (result is changed and result_with_auth is skip) or (result is skip and result_with_auth is changed) + +- name: check that service is running (part1) # py1.log content is checked below + script: "files/sendProcessStdin.py 'pys:py1' 2 \ + '{{ credentials.username }}' '{{ credentials.password }}'" + +- name: try again to start py1 service (without auth) + supervisorctl: + name: pys:py1 + state: started + config: '{{ remote_dir }}/supervisord.conf' + register: result + when: credentials.username == '' + +- name: try again to start py1 service (with auth) + supervisorctl: + name: pys:py1 + state: started + server_url: http://127.0.0.1:9001 + username: '{{ credentials.username }}' + password: '{{ credentials.password }}' + register: result_with_auth + when: credentials.username != '' + +- name: check that service is already running + assert: + that: + - (result is success and result_with_auth is skip) or (result is skip and result_with_auth is success) + - (result is not changed and result_with_auth is skip) or (result is skip and result_with_auth is not changed) + +- import_tasks: stop_supervisord.yml + +# supervisord has been stopped, check logfile +- name: check that service has done what it was expected (part 2) + shell: 'test "$(tail -2 {{ remote_dir }}/py1.log | head -1)" = ">>> 2"' + +# restart supervisord and py1 service for next tasks +- import_tasks: start_supervisord.yml + +- name: start py1 service (without auth) + supervisorctl: + name: 'pys:py1' + state: started + config: '{{ remote_dir }}/supervisord.conf' + register: result + when: credentials.username == '' + +- name: start py1 service (with auth) + supervisorctl: + name: 'pys:py1' + state: started + server_url: http://127.0.0.1:9001 + username: '{{ credentials.username }}' + password: '{{ credentials.password }}' + register: result_with_auth + when: credentials.username != '' + +- name: check that service is started + assert: + that: + - (result is success and result_with_auth is skip) or (result is skip and result_with_auth is changed) + - (result is changed and result_with_auth is skip) or (result is skip and result_with_auth is changed) + +############################################################# + +- name: Check an error occurs when wrong credentials are used + supervisorctl: + name: pys:py1 + state: started + server_url: http://127.0.0.1:9001 + username: '{{ credentials.username }}wrong_creds' + password: '{{ credentials.password }}same_here' + register: result + failed_when: result is not skip and (result is success or result is not failed) + when: credentials.username != '' + +- name: Check an error occurs when wrong URL is used + supervisorctl: + name: pys:py1 + state: started + server_url: http://127.0.0.1:9002 + register: result + failed_when: result is success or result is not failed + +- name: Check an error occurs when wrong config path is used + supervisorctl: + name: 'pys:py1' + state: started + config: '{{ remote_dir }}/supervisord_not_here.conf' + register: result + failed_when: result is success or result is not failed + +- name: Check an error occurs wrong name is used (without auth) + supervisorctl: + name: 'invalid' + state: started + config: '{{ remote_dir }}/supervisord.conf' + register: result + failed_when: result is skip or (result is success or result is not failed) + when: credentials.username == '' + +- name: Check an error occurs wrong name is used (with auth) + supervisorctl: + name: 'invalid' + state: started + config: '{{ remote_dir }}/supervisord.conf' + username: '{{ credentials.username }}wrong_creds' + password: '{{ credentials.password }}same_here' + register: result + failed_when: result is skip or (result is success or result is not failed) + when: credentials.username != '' diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_stop.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_stop.yml new file mode 100644 index 000000000..8d8fdd42a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/test_stop.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: stop py1 service + supervisorctl: + name: 'pys:py1' + state: stopped + # test with 'server_url' parameter + server_url: 'unix://{{ supervisord_sock_path.path }}/supervisord.sock' + register: result + when: credentials.username == '' + +- name: stop py1 service + supervisorctl: + name: 'pys:py1' + state: stopped + # test with unix socket + server_url: 'unix://{{ supervisord_sock_path.path }}/supervisord.sock' + username: '{{ credentials.username }}' + password: '{{ credentials.password }}' + register: result_with_auth + when: credentials.username != '' + +- command: "supervisorctl -c {{ remote_dir }}/supervisord.conf {% if credentials.username %}-u {{ credentials.username }} -p {{ credentials.password }}{% endif %} status" + +- name: check that service is stopped + assert: + that: + - (result is success and result_with_auth is skip) or (result is skip and result_with_auth is success) + - (result is changed and result_with_auth is skip) or (result is skip and result_with_auth is changed) + +- name: "check that service isn't running" + script: "files/sendProcessStdin.py 'pys:py1' 1 \ + '{{ credentials.username }}' '{{ credentials.password }}'" + register: is_py1_alive + failed_when: is_py1_alive is success + +- name: try again to stop py1 service (without auth) + supervisorctl: + name: pys:py1 + state: stopped + # test with 'server_url' parameter + server_url: 'unix://{{ supervisord_sock_path.path }}/supervisord.sock' + register: result + when: credentials.username == '' + +- name: try again to stop py1 service (with auth) + supervisorctl: + name: pys:py1 + state: stopped + # test with unix socket + server_url: 'unix://{{ supervisord_sock_path.path }}/supervisord.sock' + username: '{{ credentials.username }}' + password: '{{ credentials.password }}' + register: result_with_auth + when: credentials.username != '' + +- name: check that service is already stopped + assert: + that: + - (result is success and result_with_auth is skip) or (result is skip and result_with_auth is success) + - (result is not changed and result_with_auth is skip) or (result is skip and result_with_auth is not changed) diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Darwin.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Darwin.yml new file mode 100644 index 000000000..cf339dfd1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Darwin.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: uninstall supervisord + pip: + name: supervisor + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_FreeBSD.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_FreeBSD.yml new file mode 100644 index 000000000..cf339dfd1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_FreeBSD.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: uninstall supervisord + pip: + name: supervisor + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Linux.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Linux.yml new file mode 100644 index 000000000..442c61b72 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Linux.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: uninstall supervisor + package: + name: supervisor + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_RedHat.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_RedHat.yml new file mode 100644 index 000000000..cf339dfd1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_RedHat.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: uninstall supervisord + pip: + name: supervisor + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Suse.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Suse.yml new file mode 100644 index 000000000..cf339dfd1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_Suse.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: uninstall supervisord + pip: + name: supervisor + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_pip.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_pip.yml new file mode 100644 index 000000000..cf339dfd1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/tasks/uninstall_pip.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: uninstall supervisord + pip: + name: supervisor + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/templates/supervisord.conf b/ansible_collections/community/general/tests/integration/targets/supervisorctl/templates/supervisord.conf new file mode 100644 index 000000000..f3d36b92e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/templates/supervisord.conf @@ -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 +#} + +[supervisord] +pidfile={{ remote_dir }}/supervisord.pid +logfile={{ remote_dir }}/supervisord.log + +[program:py1] +command={{ ansible_python.executable }} -i -u - +user={{ ansible_user_id }} +autostart=false +autorestart=false +stdout_logfile={{ remote_dir }}/py1.log +redirect_stderr=yes + +[program:py2] +command={{ ansible_python.executable }} -i -u - +user={{ ansible_user_id }} +autostart=false +autorestart=false +stdout_logfile={{ remote_dir }}/py2.log +redirect_stderr=yes + +[group:pys] +programs=py1,py2 + +[unix_http_server] +file={{ supervisord_sock_path.path }}/supervisord.sock +{% if credentials.username is defined and credentials.username|default(false, boolean=true) %} +username = {{ credentials.username }} +password = {{ credentials.password }} +{% endif %} + +[inet_http_server] +port=127.0.0.1:9001 +{% if credentials.username is defined and credentials.username|default(false, boolean=true) %} +username = {{ credentials.username }} +password = {{ credentials.password }} +{% endif %} + +[supervisorctl] +serverurl=unix://{{ supervisord_sock_path.path }}/supervisord.sock + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/Debian.yml new file mode 100644 index 000000000..cba575a7c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/Debian.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 + +supervisor_service_name: supervisor diff --git a/ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/defaults.yml b/ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/defaults.yml new file mode 100644 index 000000000..1df9ae250 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/supervisorctl/vars/defaults.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 + +supervisor_service_name: supervisord diff --git a/ansible_collections/community/general/tests/integration/targets/sysrc/aliases b/ansible_collections/community/general/tests/integration/targets/sysrc/aliases new file mode 100644 index 000000000..e13fde32c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sysrc/aliases @@ -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 + +azp/posix/1 +needs/root +skip/docker +skip/osx +skip/rhel 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 new file mode 100644 index 000000000..2c45c3b1c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml @@ -0,0 +1,343 @@ +--- +# 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 on FreeBSD VMs + when: + - ansible_facts.virtualization_type != 'docker' + - ansible_facts.distribution == 'FreeBSD' + block: + - name: Cache original contents of /etc/rc.conf + shell: "cat /etc/rc.conf" + register: cached_etc_rcconf_content + + - name: Cache original contents of /boot/loader.conf + shell: "cat /boot/loader.conf" + register: cached_boot_loaderconf_content + + ## + ## sysrc - example - set mysqlpidfile + ## + - name: Configure mysql pid file + sysrc: + name: mysql_pidfile + value: /tmp/mysql.pid + register: sysrc_example1 + + - name: Configure mysql pid file (checkmode) + sysrc: + name: mysql_pidfile + value: checkmode + check_mode: true + register: sysrc_example1_checkmode + + - name: Configure mysql pid file (idempotent) + sysrc: + name: mysql_pidfile + value: /tmp/mysql.pid + register: sysrc_example1_idempotent + + - name: Get file content + shell: "cat /etc/rc.conf | egrep -v ^\\#" + register: sysrc_example1_content + + - name: Ensure sysrc updates rc.conf properly + assert: + that: + - sysrc_example1.changed + - sysrc_example1_checkmode.changed + - not sysrc_example1_idempotent.changed + - "'mysql_pidfile=\"/tmp/mysql.pid\"' in sysrc_example1_content.stdout_lines" + - "'mysql_pidfile=\"checkmode\"' not in sysrc_example1_content.stdout_lines" + + ## + ## sysrc - example - Enable accf_http kld in /boot/loader.conf + ## + - name: Enable accf_http kld in /boot/loader.conf + sysrc: + name: accf_http_load + state: present + value: "YES" + path: /boot/loader.conf + register: sysrc_example2 + + - name: Enable accf_http kld in /boot/loader.conf (checkmode) + sysrc: + name: accf_http_load + state: present + value: "NO" + path: /boot/loader.conf + check_mode: true + register: sysrc_example2_checkmode + + - name: Enable accf_http kld in /boot/loader.conf (idempotent) + sysrc: + name: accf_http_load + state: present + value: "YES" + path: /boot/loader.conf + register: sysrc_example2_idempotent + + - name: Get file content + shell: "cat /boot/loader.conf | egrep -v ^\\#" + register: sysrc_example2_content + + - name: Ensure sysrc did not change the file, but marked as changed + assert: + that: + - sysrc_example2.changed + - sysrc_example2_checkmode.changed + - not sysrc_example2_idempotent.changed + - "'accf_http_load=\"YES\"' in sysrc_example2_content.stdout_lines" + - "'accf_http_load=\"NO\"' not in sysrc_example2_content.stdout_lines" + + ## + ## sysrc - example - Add gif0 interface + ## + - name: Set cloned_interfaces + sysrc: + name: cloned_interfaces + value: "lo0" + + - name: Add gif0 interface + sysrc: + name: cloned_interfaces + state: value_present + value: "gif0" + register: sysrc_example3 + + - name: Add gif1 interface (checkmode) + sysrc: + name: cloned_interfaces + state: value_present + value: "gif1" + check_mode: true + register: sysrc_example3_checkmode + + - name: Add gif0 interface (idempotent) + sysrc: + name: cloned_interfaces + state: value_present + value: "gif0" + register: sysrc_example3_idempotent + + - name: Get file content + shell: "cat /etc/rc.conf | egrep -v ^\\#" + register: sysrc_example3_content + + - name: Ensure sysrc did not change the file, but marked as changed + assert: + that: + - sysrc_example3.changed + - sysrc_example3_checkmode.changed + - not sysrc_example3_idempotent.changed + - "'cloned_interfaces=\"lo0 gif0\"' in sysrc_example3_content.stdout_lines" + + ## + ## sysrc - example - Enable nginx in testjail + ## + - 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 + # + when: >- + ansible_distribution_version is version('12.4', '>=') and ansible_distribution_version is version('13', '<') + or ansible_distribution_version is version('13.1', '>=') + block: + - name: Setup testjail + include_tasks: setup-testjail.yml + + - name: Enable nginx in test jail + sysrc: + name: nginx_enable + value: "YES" + jail: testjail + register: sysrc_example4 + + - name: Enable nginx in test jail (checkmode) + sysrc: + name: nginx_enable + value: "NO" + jail: testjail + check_mode: true + register: sysrc_example4_checkmode + + - name: Enable nginx in test jail (idempotent) + sysrc: + name: nginx_enable + value: "YES" + jail: testjail + register: sysrc_example4_idempotent + + - name: Get file content + shell: "cat /usr/jails/testjail/etc/rc.conf | grep nginx_enable" + register: sysrc_example4_content + + - name: Ensure sysrc worked in testjail + assert: + that: + - sysrc_example4.changed + - sysrc_example4_checkmode.changed + - not sysrc_example4_idempotent.changed + - "'nginx_enable=\"YES\"' in sysrc_example4_content.stdout_lines" + always: + - name: Stop and remove testjail + failed_when: false + changed_when: false + command: "ezjail-admin delete -wf testjail" + + ## + ## sysrc - Test Absent + ## + - name: Set sysrc_absent to test removal + sysrc: + name: sysrc_absent + value: test + + - name: Remove sysrc_absent (checkmode) + sysrc: + name: sysrc_absent + state: absent + check_mode: true + register: sysrc_absent_checkmode + + - name: Remove sysrc_absent + sysrc: + name: sysrc_absent + state: absent + register: sysrc_absent + + - name: Remove sysrc_absent (idempotent) + sysrc: + name: sysrc_absent + state: absent + register: sysrc_absent_idempotent + + - name: Get file content + shell: "cat /etc/rc.conf | egrep -v ^\\#" + register: sysrc_absent_content + + - name: Ensure sysrc did as intended + assert: + that: + - sysrc_absent_checkmode.changed + - sysrc_absent.changed + - not sysrc_absent_idempotent.changed + - "'sysrc_absent=\"test\"' not in sysrc_absent_content.stdout_lines" + + ## + ## sysrc - Test alternate delimiter + ## + - name: Set sysrc_delim to known value + sysrc: + name: sysrc_delim + value: "t1,t2" + + - name: Add to value with delimiter (not-exists) + sysrc: + name: sysrc_delim_create + state: value_present + delim: "," + value: t3 + register: sysrc_delim_create + + - name: Add to value with delimiter + sysrc: + name: sysrc_delim + state: value_present + delim: "," + value: t3 + register: sysrc_delim + + - name: Add to value with delimiter (checkmode) + sysrc: + name: sysrc_delim + state: value_present + delim: "," + value: t4 + check_mode: true + register: sysrc_delim_checkmode + + - name: Add to value with delimiter (idempotent) + sysrc: + name: sysrc_delim + state: value_present + delim: "," + value: t3 + register: sysrc_delim_idempotent + + - name: Get file content + shell: "cat /etc/rc.conf | egrep -v ^\\#" + register: sysrc_delim_content + + - name: Ensure sysrc did as intended + assert: + that: + - sysrc_delim_create.changed + - sysrc_delim.changed + - sysrc_delim_checkmode.changed + - not sysrc_delim_idempotent.changed + - "'sysrc_delim=\"t1,t2,t3\"' in sysrc_delim_content.stdout_lines" + - "'sysrc_delim_create=\"t3\"' in sysrc_delim_content.stdout_lines" + + ## + ## sysrc - value_absent + ## + - name: Remove value (when not exists) + sysrc: + name: sysrc_value_absent_delete + state: value_absent + delim: "," + value: t3 + register: sysrc_value_absent_ignored + + - name: Remove value from sysrc_delim + sysrc: + name: sysrc_delim + state: value_absent + value: t3 + delim: "," + register: sysrc_value_absent + + - name: Remove value from sysrc_delim (checkmode) + sysrc: + name: sysrc_delim + state: value_absent + value: t2 + delim: "," + check_mode: true + register: sysrc_value_absent_checkmode + + - name: Remove value from sysrc_delim (idempotent + sysrc: + name: sysrc_delim + state: value_absent + value: t3 + delim: "," + register: sysrc_value_absent_idempotent + + - name: Get file content + shell: "cat /etc/rc.conf | egrep -v ^\\#" + register: sysrc_delim_content + + - name: Ensure sysrc did as intended with value_absent + assert: + that: + - not sysrc_value_absent_ignored.changed + - sysrc_value_absent.changed + - sysrc_value_absent_checkmode.changed + - not sysrc_value_absent_idempotent.changed + - "'sysrc_delim=\"t1,t2\"' in sysrc_delim_content.stdout_lines" + - "'sysrc_delim_delete' not in sysrc_delim_content.stdout_lines" + always: + - name: Restore /etc/rc.conf + copy: + content: "{{ cached_etc_rcconf_content }}" + dest: /etc/rc.conf + + - name: Restore /boot/loader.conf + copy: + content: "{{ cached_boot_loaderconf_content }}" + dest: /boot/loader.conf diff --git a/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/setup-testjail.yml b/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/setup-testjail.yml new file mode 100644 index 000000000..8aac7a430 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/setup-testjail.yml @@ -0,0 +1,73 @@ +--- +# 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 + +# +# Instructions for setting up a jail +# https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/jails-ezjail.html +# +- name: Setup cloned interfaces + lineinfile: + dest: /etc/rc.conf + regexp: ^cloned_interfaces=lo1 + line: cloned_interfaces=lo1 + +- name: Activate cloned interfaces + command: "service netif cloneup" + changed_when: false + +- name: Install ezjail + pkgng: + name: ezjail + +- name: Configure ezjail to use http + when: ansible_distribution_version is version('11.01', '>') + lineinfile: + dest: /usr/local/etc/ezjail.conf + regexp: ^ezjail_ftphost + line: ezjail_ftphost=http://ftp.freebsd.org + +- name: Configure ezjail to use archive for old freebsd releases + when: ansible_distribution_version is version('11.01', '<=') + lineinfile: + dest: /usr/local/etc/ezjail.conf + regexp: ^ezjail_ftphost + line: ezjail_ftphost=http://ftp-archive.freebsd.org + +- name: Start ezjail + ignore_errors: true + service: + name: ezjail + state: started + enabled: true + +- name: Has ezjail + register: ezjail_base_jail + stat: + path: /usr/jails/basejail + +- name: Setup ezjail base + when: not ezjail_base_jail.stat.exists + shell: "ezjail-admin install >> /tmp/ezjail.log" + changed_when: false + +- name: Has testjail + register: ezjail_test_jail + stat: + path: /usr/jails/testjail + +- name: Create testjail + when: not ezjail_test_jail.stat.exists + shell: "ezjail-admin create testjail 'lo1|127.0.1.1' >> /tmp/ezjail.log" + changed_when: false + +- name: Is testjail running + shell: "jls | grep testjail" + changed_when: false + failed_when: false + register: is_testjail_up + +- name: Start testjail + when: is_testjail_up.rc == 1 + command: "ezjail-admin start testjail" diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/.gitignore b/ansible_collections/community/general/tests/integration/targets/terraform/.gitignore new file mode 100644 index 000000000..c477f5db7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/.gitignore @@ -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 + +**/.terraform/* +*.tfstate +*.tfstate.* +.terraform.lock.hcl diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/aliases b/ansible_collections/community/general/tests/integration/targets/terraform/aliases new file mode 100644 index 000000000..1b6e4a26d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/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/1 +skip/windows +skip/aix +skip/osx +skip/macos +skip/freebsd +skip/python2 diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/main.tf b/ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/main.tf new file mode 100644 index 000000000..8b7956ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/main.tf @@ -0,0 +1,35 @@ +# 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 + +resource "null_resource" "mynullresource" { + triggers = { + # plain dictionaries + dict_name = var.dictionaries.name + dict_age = var.dictionaries.age + + # list of dicrs + join_dic_name = join(",", var.list_of_objects.*.name) + + # list-of-strings + join_list = join(",", var.list_of_strings.*) + + # testing boolean + name = var.boolean ? var.dictionaries.name : var.list_of_objects[0].name + + # top level string + sample_string_1 = var.string_type + + # nested lists + num_from_matrix = var.list_of_lists[1][2] + } + +} + +output "string_type" { + value = var.string_type +} + +output "multiline_string" { + value = var.multiline_string +} diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/variables.tf b/ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/variables.tf new file mode 100644 index 000000000..34b050747 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/files/complex_variables/variables.tf @@ -0,0 +1,62 @@ +# 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 + +variable "dictionaries" { + type = object({ + name = string + age = number + }) + description = "Same as ansible Dict" + default = { + age = 1 + name = "value" + } +} + +variable "list_of_strings" { + type = list(string) + description = "list of strings" + validation { + condition = (var.list_of_strings[1] == "cli specials\"&$%@#*!(){}[]:\"\" \\\\") + error_message = "Strings do not match." + } +} + +variable "list_of_objects" { + type = list(object({ + name = string + age = number + })) + validation { + condition = (var.list_of_objects[1].name == "cli specials\"&$%@#*!(){}[]:\"\" \\\\") + error_message = "Strings do not match." + } +} + +variable "boolean" { + type = bool + description = "boolean" + +} + +variable "string_type" { + type = string + validation { + condition = (var.string_type == "cli specials\"&$%@#*!(){}[]:\"\" \\\\") + error_message = "Strings do not match." + } +} + +variable "multiline_string" { + type = string + validation { + condition = (var.multiline_string == "one\ntwo\n") + error_message = "Strings do not match." + } +} + +variable "list_of_lists" { + type = list(list(any)) + default = [ [ 1 ], [1, 2, 3], [3] ] +} diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/terraform/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/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/terraform/tasks/complex_variables.yml b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/complex_variables.yml new file mode 100644 index 000000000..9788a3eed --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/complex_variables.yml @@ -0,0 +1,60 @@ +--- +# 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 terraform project directory (complex variables) + ansible.builtin.file: + path: "{{ terraform_project_dir }}/complex_vars" + state: directory + mode: 0755 + +- name: copy terraform files to work space + ansible.builtin.copy: + src: "complex_variables/{{ item }}" + dest: "{{ terraform_project_dir }}/complex_vars/{{ item }}" + with_items: + - main.tf + - variables.tf + +# This task would test the various complex variable structures of the with the +# terraform null_resource +- name: test complex variables + community.general.terraform: + project_path: "{{ terraform_project_dir }}/complex_vars" + binary_path: "{{ terraform_binary_path }}" + force_init: true + complex_vars: true + variables: + dictionaries: + name: "kosala" + age: 99 + list_of_strings: + - "kosala" + - 'cli specials"&$%@#*!(){}[]:"" \\' + - "xxx" + - "zzz" + list_of_objects: + - name: "kosala" + age: 99 + - name: 'cli specials"&$%@#*!(){}[]:"" \\' + age: 0.1 + - name: "zzz" + age: 9.789 + - name: "lll" + age: 1000 + boolean: true + string_type: 'cli specials"&$%@#*!(){}[]:"" \\' + multiline_string: | + one + two + list_of_lists: + - [ 1 ] + - [ 11, 12, 13 ] + - [ 2 ] + - [ 3 ] + state: present + register: terraform_init_result + +- assert: + that: terraform_init_result is not failed 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 new file mode 100644 index 000000000..1c66990be --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml @@ -0,0 +1,67 @@ +--- +# 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 block checks and registers Terraform version of the binary found in path. + +- name: Check for existing Terraform in path + block: + - name: Check if terraform is present in path + ansible.builtin.command: "command -v terraform" + register: terraform_binary_path + ignore_errors: true + + - name: Check Terraform version + ansible.builtin.command: terraform version + register: terraform_version_output + when: terraform_binary_path.rc == 0 + + - name: Set terraform version + ansible.builtin.set_fact: + terraform_version_installed: "{{ terraform_version_output.stdout | regex_search('(?!Terraform.*v)([0-9]+\\.[0-9]+\\.[0-9]+)') }}" + when: terraform_version_output.changed + +# This block handles the tasks of installing the Terraform binary. This happens if there is no existing +# terraform in $PATH OR version does not match `terraform_version`. + +- name: Execute Terraform install tasks + block: + + - name: Install Terraform + ansible.builtin.debug: + msg: "Installing terraform {{ terraform_version }}, found: {{ terraform_version_installed | default('no terraform binary found') }}." + + - name: Ensure unzip is present + ansible.builtin.package: + name: unzip + state: present + + - name: Install Terraform binary + ansible.builtin.unarchive: + src: "{{ terraform_url }}" + dest: "{{ remote_tmp_dir }}" + mode: 0755 + remote_src: true + validate_certs: "{{ validate_certs }}" + + when: terraform_version_installed is not defined or terraform_version_installed != terraform_version + +# This sets `terraform_binary_path` to coalesced output of first non-empty string in this order: +# path from the 'Check if terraform is present in path' task, and lastly, the fallback path. + +- name: Set path to terraform binary + ansible.builtin.set_fact: + terraform_binary_path: "{{ terraform_binary_path.stdout or remote_tmp_dir ~ '/terraform' }}" + +- name: Loop over provider upgrade test tasks + ansible.builtin.include_tasks: test_provider_upgrade.yml + vars: + tf_provider: "{{ terraform_provider_versions[provider_index] }}" + loop: "{{ terraform_provider_versions }}" + loop_control: + index_var: provider_index + +- name: Test Complex Varibles + ansible.builtin.include_tasks: complex_variables.yml diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/tasks/test_provider_upgrade.yml b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/test_provider_upgrade.yml new file mode 100644 index 000000000..b20182c9f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/test_provider_upgrade.yml @@ -0,0 +1,35 @@ +--- +# 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 terraform project directory (provider upgrade) + file: + path: "{{ terraform_project_dir }}/{{ item['name'] }}" + state: directory + mode: 0755 + loop: "{{ terraform_provider_versions }}" + loop_control: + index_var: provider_index + +- name: Output terraform provider test project + ansible.builtin.template: + src: templates/provider_test/main.tf.j2 + dest: "{{ terraform_project_dir }}/{{ tf_provider['name'] }}/main.tf" + force: true + register: terraform_provider_hcl + +# The purpose of this task is to init terraform multiple times with different provider module +# versions, so that we can verify that provider upgrades during init work as intended. + +- name: Init Terraform configuration with pinned provider version + community.general.terraform: + project_path: "{{ terraform_provider_hcl.dest | dirname }}" + binary_path: "{{ terraform_binary_path }}" + force_init: true + provider_upgrade: "{{ terraform_provider_upgrade }}" + state: present + register: terraform_init_result + +- assert: + that: terraform_init_result is not failed diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/templates/provider_test/main.tf.j2 b/ansible_collections/community/general/tests/integration/targets/terraform/templates/provider_test/main.tf.j2 new file mode 100644 index 000000000..886a0c2de --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/templates/provider_test/main.tf.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 +#} +terraform { + required_providers { + {{ tf_provider['name'] }} = { + source = "{{ tf_provider['source'] }}" + version = "{{ tf_provider['version'] }}" + } + } +} diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/terraform/vars/main.yml new file mode 100644 index 000000000..1032adee4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/terraform/vars/main.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 + +# Terraform version that will be downloaded +terraform_version: 1.1.7 + +# Architecture of the downloaded Terraform release (needs to match target testing platform) + +terraform_arch: "{{ ansible_system | lower }}_{{terraform_arch_map[ansible_architecture] }}" + +# URL of where the Terraform binary will be downloaded from +terraform_url: "https://releases.hashicorp.com/terraform/{{ terraform_version }}/terraform_{{ terraform_version }}_{{ terraform_arch }}.zip" + +# Controls whether the unarchive task will validate TLS certs of the Terraform binary host +validate_certs: true + +# Directory where Terraform tests will be created +terraform_project_dir: "{{ remote_tmp_dir }}/tf_provider_test" + +# Controls whether terraform init will use the `-upgrade` flag +terraform_provider_upgrade: true + +# list of dicts containing Terraform providers that will be tested +# The null provider is a good candidate, as it's small and has no external dependencies +terraform_provider_versions: + - name: "null" + source: "hashicorp/null" + version: ">=2.0.0, < 3.0.0" + - name: "null" + source: "hashicorp/null" + version: ">=3.0.0" + +# mapping between values returned from ansible_architecture and arch names used by golang builds of Terraform +# see https://www.terraform.io/downloads + +terraform_arch_map: + x86_64: amd64 + arm64: arm64 diff --git a/ansible_collections/community/general/tests/integration/targets/test_a_module/aliases b/ansible_collections/community/general/tests/integration/targets/test_a_module/aliases new file mode 100644 index 000000000..343f119da --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/test_a_module/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/3 diff --git a/ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/galaxy.yml b/ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/galaxy.yml new file mode 100644 index 000000000..2243e0dba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/galaxy.yml @@ -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 + +namespace: testns +name: testcoll +version: 0.0.1 +authors: + - Ansible (https://github.com/ansible) +description: null +tags: [community] diff --git a/ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py b/ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py new file mode 100644 index 000000000..e7f1a987a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/test_a_module/collections/ansible_collections/testns/testcoll/plugins/modules/collection_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: collection_module +short_description: Test collection module +description: + - This is a test module in a local collection. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/test_a_module/library/local_module.py b/ansible_collections/community/general/tests/integration/targets/test_a_module/library/local_module.py new file mode 100644 index 000000000..9e9e649cb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/test_a_module/library/local_module.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Felix Fontein +# 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 + +DOCUMENTATION = ''' +--- +module: local_module +short_description: Test local module +description: + - This is a test module locally next to a playbook. +author: "Felix Fontein (@felixfontein)" +options: {} +''' + +EXAMPLES = ''' # ''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + AnsibleModule(argument_spec={}).exit_json() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/integration/targets/test_a_module/runme.sh b/ansible_collections/community/general/tests/integration/targets/test_a_module/runme.sh new file mode 100755 index 000000000..118abbc29 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/test_a_module/runme.sh @@ -0,0 +1,20 @@ +#!/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 + +# The collection loader ignores paths which have more than one ansible_collections in it. +# That's why we have to copy this directory to a temporary place and run the test there. + +# Create temporary folder +TEMPDIR=$(mktemp -d) +trap '{ rm -rf ${TEMPDIR}; }' EXIT + +cp -r . "${TEMPDIR}" +cd "${TEMPDIR}" + +ansible-playbook runme.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/test_a_module/runme.yml b/ansible_collections/community/general/tests/integration/targets/test_a_module/runme.yml new file mode 100644 index 000000000..4b7a5ec2c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/test_a_module/runme.yml @@ -0,0 +1,42 @@ +--- +# 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 + tasks: + - name: Test a_module + assert: + that: + # Modules/actions that do not exist + - "'foo_bar' is not community.general.a_module" + - "'foo.bar.baz' is not community.general.a_module" + # Short name and FQCN for builtin and other collections + - "'file' is community.general.a_module" + - "'set_fact' is community.general.a_module" + - "'ansible.builtin.file' is community.general.a_module" + - "'ansible.builtin.set_fact' is community.general.a_module" + - "'ansible.builtin.foo_bar' is not community.general.a_module" + - "'community.crypto.acme_certificate' is community.general.a_module" + - "'community.crypto.openssl_privatekey_pipe' is community.general.a_module" + - "'community.crypto.foo_bar' is not community.general.a_module" + # Modules from this collection (that exist or not) + - "'community.general.ufw' is community.general.a_module" + - "'community.general.foooo_really_does_not_exist' is not community.general.a_module" + # Local module + - "'local_module' is community.general.a_module" + # Local collection module (that exist or not) + - "'testns.testcoll.collection_module' is community.general.a_module" + - "'testns.testcoll.foobar' is not community.general.a_module" + + - name: Test a_module in case of routing + assert: + that: + # Redirected module + - "'ufw' is community.general.a_module" + # Redirected module where target collection does not exist + # (the target collection must not have been installed in CI!) + - "'onyx_pfc_interface' is not community.general.a_module" + # Tombstoned module + - "'community.general.docker_image_facts' is not community.general.a_module" + when: ansible_version.string is version('2.10.0', '>=') diff --git a/ansible_collections/community/general/tests/integration/targets/timezone/aliases b/ansible_collections/community/general/tests/integration/targets/timezone/aliases new file mode 100644 index 000000000..007bed538 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/timezone/aliases @@ -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 + +azp/posix/1 +destructive +skip/aix +skip/osx +skip/macos diff --git a/ansible_collections/community/general/tests/integration/targets/timezone/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/timezone/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/timezone/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/timezone/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml new file mode 100644 index 000000000..3644eeafa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml @@ -0,0 +1,96 @@ +--- +#################################################################### +# 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 + +# Because hwclock usually isn't available inside Docker containers in Shippable +# these tasks will detect if hwclock works and only run hwclock tests if it is +# supported. That is why it is recommended to run these tests locally with +# `--docker-privileged` on centos6, centos7 and ubuntu1404 images. Example +# command to run on centos6: +# +# ansible-test integration --docker centos6 --docker-privileged -v timezone + +## +## set path to timezone config files +## + +- name: set config file path on Debian + set_fact: + timezone_config_file: '/etc/timezone' + when: ansible_os_family == 'Debian' + +- name: set config file path on RedHat + set_fact: + timezone_config_file: '/etc/sysconfig/clock' + when: ansible_os_family == 'RedHat' + +## +## set path to hwclock config files +## + +- name: set config file path on Debian + set_fact: + hwclock_config_file: '/etc/default/rcS' + when: ansible_os_family == 'Debian' + +- name: set config file path on RedHat + set_fact: + hwclock_config_file: '/etc/sysconfig/clock' + when: ansible_os_family == 'RedHat' + +#### +#### timezone tests +#### + +- name: make sure diffutils are installed on ArchLinux + package: + name: diffutils + state: present + when: ansible_distribution == 'Archlinux' + +- name: make sure tzdata is installed on Alpine + package: + name: tzdata + state: present + when: ansible_distribution == 'Alpine' + +- name: make sure the dbus service is started under systemd + systemd: + name: dbus + state: started + when: + - ansible_service_mgr == 'systemd' + - ansible_distribution == 'Fedora' + - ansible_facts.distribution_major_version is version('31', '<') + + +- name: Run tests + # Skip tests on Fedora 31 and 32 because dbus fails to start unless the container is run in privileged mode. + # Even then, it starts unreliably. This may be due to the move to cgroup v2 in Fedora 31 and 32. + # https://www.redhat.com/sysadmin/fedora-31-control-group-v2 + when: + - ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['Fedora31', 'Fedora32'] + - not (ansible_os_family == 'Alpine') # TODO + block: + - name: set timezone to Etc/UTC + timezone: + name: Etc/UTC + register: original_timezone + + - name: Value of original_timezone + debug: + msg: "{{ original_timezone }}" + + - block: + - include_tasks: test.yml + always: + - name: Restore original system timezone - {{ original_timezone.diff.before.name }} + timezone: + name: "{{ original_timezone.diff.before.name }}" + when: original_timezone is changed diff --git a/ansible_collections/community/general/tests/integration/targets/timezone/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/timezone/tasks/test.yml new file mode 100644 index 000000000..975526800 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/timezone/tasks/test.yml @@ -0,0 +1,612 @@ +--- +# 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 + +## +## test setting timezone, idempotency and checkmode +## + +- name: set timezone to Australia/Brisbane (checkmode) + timezone: + name: Australia/Brisbane + check_mode: true + register: timezone_set_checkmode + +- name: ensure timezone reported as changed in checkmode + assert: + that: + - timezone_set_checkmode.changed + - timezone_set_checkmode.diff.after.name == 'Australia/Brisbane' + - timezone_set_checkmode.diff.before.name == 'Etc/UTC' + +- name: ensure checkmode didn't change the timezone + command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane + register: result + failed_when: result is not failed + changed_when: false + +- name: ensure that checkmode didn't update the timezone in the config file + command: egrep '^(TIME)?ZONE="Etc/UTC"' {{ timezone_config_file }} + when: + - ansible_service_mgr != 'systemd' + - ansible_os_family == 'RedHat' + +- name: ensure that checkmode didn't update the timezone in the config file + command: egrep '^Etc/UTC' {{ timezone_config_file }} + when: + - ansible_service_mgr != 'systemd' + - ansible_os_family == 'Debian' + +- name: set timezone to Australia/Brisbane + timezone: + name: Australia/Brisbane + register: timezone_set + +- name: ensure timezone changed + assert: + that: + - timezone_set.changed + - timezone_set.diff.after.name == 'Australia/Brisbane' + - timezone_set.diff.before.name == 'Etc/UTC' + +- name: ensure that the timezone is actually set + command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane + changed_when: false + +- name: ensure that the timezone is updated in the config file + command: egrep '^(TIME)?ZONE="Australia/Brisbane"' {{ timezone_config_file }} + when: + - ansible_service_mgr != 'systemd' + - ansible_os_family == 'RedHat' + +- name: ensure that the timezone is updated in the config file + command: egrep '^Australia/Brisbane' {{ timezone_config_file }} + when: + - ansible_service_mgr != 'systemd' + - ansible_os_family == 'Debian' + +- name: set timezone to Australia/Brisbane again + timezone: + name: Australia/Brisbane + register: timezone_again + +- name: ensure timezone idempotency + assert: + that: + - not timezone_again.changed + +- name: set timezone to Australia/Brisbane again in checkmode + timezone: + name: Australia/Brisbane + register: timezone_again_checkmode + +- name: set timezone idempotency (checkmode) + assert: + that: + - not timezone_again_checkmode.changed + +## +## tests for same timezones with different names +## + +- name: check dpkg-reconfigure + shell: type dpkg-reconfigure + register: check_dpkg_reconfigure + ignore_errors: true + changed_when: false + +- name: check timedatectl + shell: type timedatectl && timedatectl + register: check_timedatectl + ignore_errors: true + changed_when: false + +- block: + - name: set timezone to Etc/UTC + timezone: + name: Etc/UTC + + - name: change timezone from Etc/UTC to UTC + timezone: + name: UTC + register: timezone_etcutc_to_utc + + - name: check timezone changed from Etc/UTC to UTC + assert: + that: + - timezone_etcutc_to_utc.changed + - timezone_etcutc_to_utc.diff.before.name == 'Etc/UTC' + - timezone_etcutc_to_utc.diff.after.name == 'UTC' + + - name: change timezone from UTC to Etc/UTC + timezone: + name: Etc/UTC + register: timezone_utc_to_etcutc + + - name: check timezone changed from UTC to Etc/UTC + assert: + that: + - timezone_utc_to_etcutc.changed + - timezone_utc_to_etcutc.diff.before.name == 'UTC' + - timezone_utc_to_etcutc.diff.after.name == 'Etc/UTC' + + when: + # FIXME: Due to the bug of the dpkg-reconfigure, those tests failed on non-systemd debian + - check_dpkg_reconfigure.rc != 0 or check_timedatectl.rc == 0 + +## +## no systemd tests for timezone +## + +- block: + ## + ## test with empty config file + ## + + - name: empty config file + command: cp /dev/null {{ timezone_config_file }} + + - name: set timezone to Europe/Belgrade (empty config file) + timezone: + name: Europe/Belgrade + register: timezone_empty_conf + + - name: check if timezone set (empty config file) + assert: + that: + - timezone_empty_conf.changed + - timezone_empty_conf.diff.after.name == 'Europe/Belgrade' + - timezone_empty_conf.diff.before.name == 'n/a' + + - name: check if the timezone is actually set (empty config file) + command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade + changed_when: false + + + ## + ## test with deleted config file + ## + + - name: remove config file + file: + path: '{{ timezone_config_file }}' + state: absent + + - name: set timezone to Europe/Belgrade (no config file) + timezone: + name: Europe/Belgrade + register: timezone_missing_conf + + - name: check if timezone set (no config file) + assert: + that: + - timezone_missing_conf.changed + - timezone_missing_conf.diff.after.name == 'Europe/Belgrade' + - timezone_missing_conf.diff.before.name == 'n/a' + + - name: check if the timezone is actually set (no config file) + command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade + changed_when: false + + + ## + ## test with /etc/localtime as symbolic link to a zoneinfo file + ## + + - name: create symlink /etc/locatime -> /usr/share/zoneinfo/Etc/UTC + file: + src: /usr/share/zoneinfo/Etc/UTC + dest: /etc/localtime + state: link + force: true + + - name: set timezone to Europe/Belgrade (over symlink) + timezone: + name: Europe/Belgrade + register: timezone_symllink + + - name: check if timezone set (over symlink) + assert: + that: + - timezone_symllink.changed + - timezone_symllink.diff.after.name == 'Europe/Belgrade' + - timezone_symllink.diff.before.name == 'Etc/UTC' + + - name: check if the timezone is actually set (over symlink) + command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade + changed_when: false + + + ## + ## test with /etc/localtime as broken symbolic link + ## + + - name: set timezone to a broken symlink + file: + src: /tmp/foo + dest: /etc/localtime + state: link + force: true + + - name: set timezone to Europe/Belgrade (over broken symlink) + timezone: + name: Europe/Belgrade + register: timezone_symllink_broken + + - name: check if timezone set (over broken symlink) + assert: + that: + - timezone_symllink_broken.changed + - timezone_symllink_broken.diff.after.name == 'Europe/Belgrade' + - timezone_symllink_broken.diff.before.name == 'n/a' + + - name: check if the timezone is actually set (over broken symlink) + command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade + changed_when: false + + + ## + ## test with /etc/localtime set manually using copy + ## + + - name: set timezone manually by coping zone info file to /etc/localtime + copy: + src: /usr/share/zoneinfo/Etc/UTC + dest: /etc/localtime + remote_src: true + + - name: set timezone to Europe/Belgrade (over copied file) + timezone: + name: Europe/Belgrade + register: timezone_copied + + - name: check if timezone set (over copied file) + assert: + that: + - timezone_copied.changed + - timezone_copied.diff.after.name == 'Europe/Belgrade' + - timezone_copied.diff.before.name == 'n/a' + + - name: check if the timezone is actually set (over copied file) + command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade + changed_when: false + when: + - ansible_service_mgr != 'systemd' + - timezone_config_file is defined + + +#### +#### hwclock tests +#### + +- name: check if hwclock is supported in the environment + command: hwclock --test + register: hwclock_test + ignore_errors: true + +- name: check if timedatectl works in the environment + command: timedatectl + register: timedatectl_test + ignore_errors: true + +- name: + set_fact: + hwclock_supported: '{{ hwclock_test is successful or (timedatectl_test is successful and "RTC time: n/a" not in timedatectl_test.stdout) }}' +## +## test set hwclock, idempotency and checkmode +## + +- block: + - name: set hwclock to local + timezone: + hwclock: local + + - name: set hwclock to UTC (checkmode) + timezone: + hwclock: UTC + check_mode: true + register: hwclock_set_checkmode + + - name: ensure hwclock reported as changed (checkmode) + assert: + that: + - hwclock_set_checkmode.changed + - hwclock_set_checkmode.diff.after.hwclock == 'UTC' + - hwclock_set_checkmode.diff.before.hwclock == 'local' + + - block: + - name: ensure that checkmode didn't update hwclock in /etc/adjtime + command: grep ^UTC /etc/adjtime + register: result + failed_when: result is not failed + + - name: ensure that checkmode didn't update hwclock the config file + command: grep ^UTC=no {{ hwclock_config_file }} + when: ansible_service_mgr != 'systemd' + + - name: set hwclock to UTC + timezone: + hwclock: UTC + register: hwclock_set + + - name: ensure hwclock changed + assert: + that: + - hwclock_set.changed + - hwclock_set.diff.after.hwclock == 'UTC' + - hwclock_set.diff.before.hwclock == 'local' + + - block: + - name: ensure that hwclock is updated in /etc/adjtime + command: grep ^UTC /etc/adjtime + + - name: ensure that hwclock is updated in the config file + command: grep ^UTC=yes {{ hwclock_config_file }} + when: ansible_service_mgr != 'systemd' + + - name: set hwclock to RTC again + timezone: + hwclock: UTC + register: hwclock_again + + - name: set hwclock idempotency + assert: + that: + - not hwclock_again.changed + + - name: set hwclock to RTC again (checkmode) + timezone: + hwclock: UTC + check_mode: true + register: hwclock_again_checkmode + + - name: set hwclock idempotency (checkmode) + assert: + that: + - not hwclock_again_checkmode.changed + + + ## + ## no systemd tests for hwclock + ## + + - block: + ## + ## test set hwclock with both /etc/adjtime and conf file deleted + ## + + - name: remove /etc/adjtime and conf file + file: + path: '{{ item }}' + state: absent + with_items: + - /etc/adjtime + - '{{ hwclock_config_file }}' + + - name: set hwclock to UTC with deleted /etc/adjtime and conf file + timezone: + hwclock: UTC + register: hwclock_set_utc_deleted_adjtime_and_conf + + - name: ensure hwclock changed with deleted /etc/adjtime and conf + assert: + that: + - hwclock_set_utc_deleted_adjtime_and_conf.changed + - hwclock_set_utc_deleted_adjtime_and_conf.diff.after.hwclock == 'UTC' + - hwclock_set_utc_deleted_adjtime_and_conf.diff.before.hwclock == 'n/a' + + + ## + ## test set hwclock with /etc/adjtime deleted + ## + + - name: remove /etc/adjtime + file: + path: '{{ item }}' + state: absent + with_items: + - /etc/adjtime + + - name: set hwclock to UTC with deleted /etc/adjtime + timezone: + hwclock: UTC + register: hwclock_set_utc_deleted_adjtime_utc + + - name: ensure hwclock changed with deleted /etc/adjtime + assert: + that: + - not hwclock_set_utc_deleted_adjtime_utc.changed + - hwclock_set_utc_deleted_adjtime_utc.diff.after.hwclock == 'UTC' + - hwclock_set_utc_deleted_adjtime_utc.diff.before.hwclock == 'UTC' + + - name: set hwclock to LOCAL with deleted /etc/adjtime + timezone: + hwclock: local + register: hwclock_set_local_deleted_adjtime_local + + - name: ensure hwclock changed to LOCAL with deleted /etc/adjtime + assert: + that: + - hwclock_set_local_deleted_adjtime_local.changed + - hwclock_set_local_deleted_adjtime_local.diff.after.hwclock == 'local' + - hwclock_set_local_deleted_adjtime_local.diff.before.hwclock == 'UTC' + + + ## + ## test set hwclock with conf file deleted + ## + + - name: remove conf file + file: + path: '{{ item }}' + state: absent + with_items: + - '{{ hwclock_config_file }}' + + - name: set hwclock to UTC with deleted conf + timezone: + hwclock: UTC + register: hwclock_set_utc_deleted_conf + + - name: ensure hwclock changed with deleted /etc/adjtime + assert: + that: + - hwclock_set_utc_deleted_conf.changed + - hwclock_set_utc_deleted_conf.diff.after.hwclock == 'UTC' + - hwclock_set_utc_deleted_conf.diff.before.hwclock == 'n/a' + + + ## + ## test set hwclock with /etc/adjtime missing UTC/LOCAL strings + ## + + - name: create /etc/adjtime without UTC/LOCAL + copy: + content: '0.0 0 0\n0' + dest: /etc/adjtime + + - name: set hwclock to UTC with broken /etc/adjtime + timezone: + hwclock: UTC + register: hwclock_set_utc_broken_adjtime + + - name: ensure hwclock doesn't report changed with broken /etc/adjtime + assert: + that: + - not hwclock_set_utc_broken_adjtime.changed + - hwclock_set_utc_broken_adjtime.diff.after.hwclock == 'UTC' + - hwclock_set_utc_broken_adjtime.diff.before.hwclock == 'UTC' + + - name: set hwclock to LOCAL with broken /etc/adjtime + timezone: + hwclock: local + register: hwclock_set_local_broken_adjtime + + - name: ensure hwclock changed to LOCAL with broken /etc/adjtime + assert: + that: + - hwclock_set_local_broken_adjtime.changed + - hwclock_set_local_broken_adjtime.diff.after.hwclock == 'local' + - hwclock_set_local_broken_adjtime.diff.before.hwclock == 'UTC' + when: + - ansible_service_mgr != 'systemd' + - hwclock_config_file is defined + + #### + #### timezone + hwclock tests + #### + + ## + ## test set timezone and hwclock, idempotency and checkmode + ## + + - name: set timezone to Etc/UTC and hwclock to local + timezone: + name: Etc/UTC + hwclock: local + + - name: set timezone to Europe/Belgrade and hwclock to UTC (checkmode) + timezone: + name: Europe/Belgrade + hwclock: UTC + check_mode: true + register: tzclock_set_checkmode + + - name: ensure timezone and hwclock reported as changed in checkmode + assert: + that: + - tzclock_set_checkmode.changed + - tzclock_set_checkmode.diff.after.name == 'Europe/Belgrade' + - tzclock_set_checkmode.diff.before.name == 'Etc/UTC' + - tzclock_set_checkmode.diff.after.hwclock == 'UTC' + - tzclock_set_checkmode.diff.before.hwclock == 'local' + + - name: ensure checkmode didn't change the timezone + command: cmp /etc/localtime /usr/share/zoneinfo/Australia/Brisbane + register: result + failed_when: result is not failed + changed_when: false + + - block: + - name: ensure that checkmode didn't update the timezone in the config file + command: egrep '^(TIME)?ZONE="Etc/UTC"' {{ timezone_config_file }} + when: + - ansible_os_family == 'RedHat' + + - name: ensure that checkmode didn't update the timezone in the config file + command: egrep '^Etc/UTC' {{ timezone_config_file }} + when: + - ansible_os_family == 'Debian' + + - name: ensure that checkmode didn't update hwclock in /etc/adjtime + command: grep ^UTC /etc/adjtime + register: result + failed_when: result is not failed + + - name: ensure that checkmode didn't update hwclock the config file + command: grep ^UTC=no {{ hwclock_config_file }} + when: ansible_service_mgr != 'systemd' + + - name: set timezone to Europe/Belgrade and hwclock to UTC + timezone: + name: Europe/Belgrade + hwclock: UTC + register: tzclock_set + + - name: ensure timezone and hwclock changed + assert: + that: + - tzclock_set.changed + - tzclock_set.diff.after.name == 'Europe/Belgrade' + - tzclock_set.diff.before.name == 'Etc/UTC' + - tzclock_set.diff.after.hwclock == 'UTC' + - tzclock_set.diff.before.hwclock == 'local' + + - name: ensure that the timezone is actually set + command: cmp /etc/localtime /usr/share/zoneinfo/Europe/Belgrade + changed_when: false + + - block: + - name: ensure that the timezone is updated in the config file + command: egrep '^(TIME)?ZONE="Europe/Belgrade"' {{ timezone_config_file }} + when: + - ansible_os_family == 'RedHat' + + - name: ensure that the timezone is updated in the config file + command: egrep 'Europe/Belgrade' {{ timezone_config_file }} + when: + - ansible_os_family == 'Debian' + + - name: ensure that hwclock is updated in /etc/adjtime + command: grep ^UTC /etc/adjtime + + - name: ensure that hwclock is updated in the config file + command: grep ^UTC=yes {{ hwclock_config_file }} + when: ansible_service_mgr != 'systemd' + + - name: set timezone to Europe/Belgrade and hwclock to UTC again + timezone: + name: Europe/Belgrade + hwclock: UTC + register: tzclock_set_again + + - name: set timezone and hwclock idempotency + assert: + that: + - not tzclock_set_again.changed + + - name: set timezone to Europe/Belgrade and hwclock to UTC again (checkmode) + timezone: + name: Europe/Belgrade + hwclock: UTC + register: tzclock_set_again_checkmode + + - name: set timezone and hwclock idempotency in checkmode + assert: + that: + - not tzclock_set_again_checkmode.changed + + when: + - ansible_system == 'Linux' + - hwclock_supported diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/aliases b/ansible_collections/community/general/tests/integration/targets/ufw/aliases new file mode 100644 index 000000000..2ef1a4133 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/aliases @@ -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 + +azp/posix/2 +azp/posix/vm +skip/aix +skip/osx +skip/macos +skip/freebsd +skip/rhel8.0 # FIXME +skip/rhel9.0 # FIXME +skip/rhel9.1 # FIXME +skip/docker +needs/root +needs/target/setup_epel +destructive diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/ufw/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/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/ufw/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/main.yml new file mode 100644 index 000000000..5fba2fa4d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/main.yml @@ -0,0 +1,45 @@ +--- +#################################################################### +# 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 + +# Make sure ufw is installed +- name: Install EPEL repository (RHEL only) + include_role: + name: setup_epel + when: + - ansible_distribution in ['RedHat', 'CentOS'] + - ansible_distribution_major_version is version('9', '<') +- name: Install iptables (SuSE only) + package: + name: iptables + become: true + when: ansible_os_family == 'Suse' +- name: Install ufw + become: true + package: + name: ufw + +# Run the tests +- block: + - include_tasks: run-test.yml + with_fileglob: + - "tests/*.yml" + become: true + + # Cleanup + always: + - pause: + # ufw creates backups of the rule files with a timestamp; if reset is called + # twice in a row fast enough (so that both timestamps are taken in the same second), + # the second call will notice that the backup files are already there and fail. + # Waiting one second fixes this problem. + seconds: 1 + - name: Reset ufw to factory defaults and disable + ufw: + state: reset diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/tasks/run-test.yml b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/run-test.yml new file mode 100644 index 000000000..e9e7b33f5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/run-test.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 + +- pause: + # ufw creates backups of the rule files with a timestamp; if reset is called + # twice in a row fast enough (so that both timestamps are taken in the same second), + # the second call will notice that the backup files are already there and fail. + # Waiting one second fixes this problem. + seconds: 1 +- name: Reset ufw to factory defaults + ufw: + state: reset +- name: Disable ufw + ufw: + # Some versions of ufw have a bug which won't disable on reset. + # That's why we explicitly deactivate here. See + # https://bugs.launchpad.net/ufw/+bug/1810082 + state: disabled +- name: "Loading tasks from {{ item }}" + include_tasks: "{{ item }}" +- name: Reset to factory defaults + ufw: + state: reset diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/basic.yml b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/basic.yml new file mode 100644 index 000000000..8c179d7ae --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/basic.yml @@ -0,0 +1,406 @@ +--- +# 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 it is off + ufw: + state: disabled +- name: Enable (check mode) + ufw: + state: enabled + check_mode: true + register: enable_check +- name: Enable + ufw: + state: enabled + register: enable +- name: Enable (idempotency) + ufw: + state: enabled + register: enable_idem +- name: Enable (idempotency, check mode) + ufw: + state: enabled + check_mode: true + register: enable_idem_check +- assert: + that: + - enable_check is changed + - enable is changed + - enable_idem is not changed + - enable_idem_check is not changed + +# ############################################ +- name: ipv4 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + check_mode: true + register: ipv4_allow_check +- name: ipv4 allow + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + register: ipv4_allow +- name: ipv4 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + register: ipv4_allow_idem +- name: ipv4 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + check_mode: true + register: ipv4_allow_idem_check +- assert: + that: + - ipv4_allow_check is changed + - ipv4_allow is changed + - ipv4_allow_idem is not changed + - ipv4_allow_idem_check is not changed + +# ############################################ +- name: delete ipv4 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + check_mode: true + register: delete_ipv4_allow_check +- name: delete ipv4 allow + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + register: delete_ipv4_allow +- name: delete ipv4 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + register: delete_ipv4_allow_idem +- name: delete ipv4 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + check_mode: true + register: delete_ipv4_allow_idem_check +- assert: + that: + - delete_ipv4_allow_check is changed + - delete_ipv4_allow is changed + - delete_ipv4_allow_idem is not changed + - delete_ipv4_allow_idem_check is not changed + +# ############################################ +- name: ipv6 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + check_mode: true + register: ipv6_allow_check +- name: ipv6 allow + ufw: + rule: allow + port: 23 + to_ip: "::" + register: ipv6_allow +- name: ipv6 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: "::" + register: ipv6_allow_idem +- name: ipv6 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + check_mode: true + register: ipv6_allow_idem_check +- assert: + that: + - ipv6_allow_check is changed + - ipv6_allow is changed + - ipv6_allow_idem is not changed + - ipv6_allow_idem_check is not changed + +# ############################################ +- name: delete ipv6 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + check_mode: true + register: delete_ipv6_allow_check +- name: delete ipv6 allow + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + register: delete_ipv6_allow +- name: delete ipv6 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + register: delete_ipv6_allow_idem +- name: delete ipv6 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + check_mode: true + register: delete_ipv6_allow_idem_check +- assert: + that: + - delete_ipv6_allow_check is changed + - delete_ipv6_allow is changed + - delete_ipv6_allow_idem is not changed + - delete_ipv6_allow_idem_check is not changed + + +# ############################################ +- name: ipv4 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + check_mode: true + register: ipv4_allow_check +- name: ipv4 allow + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + register: ipv4_allow +- name: ipv4 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + register: ipv4_allow_idem +- name: ipv4 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + check_mode: true + register: ipv4_allow_idem_check +- assert: + that: + - ipv4_allow_check is changed + - ipv4_allow is changed + - ipv4_allow_idem is not changed + - ipv4_allow_idem_check is not changed + +# ############################################ +- name: delete ipv4 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + check_mode: true + register: delete_ipv4_allow_check +- name: delete ipv4 allow + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + register: delete_ipv4_allow +- name: delete ipv4 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + register: delete_ipv4_allow_idem +- name: delete ipv4 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: 0.0.0.0 + delete: true + check_mode: true + register: delete_ipv4_allow_idem_check +- assert: + that: + - delete_ipv4_allow_check is changed + - delete_ipv4_allow is changed + - delete_ipv4_allow_idem is not changed + - delete_ipv4_allow_idem_check is not changed + +# ############################################ +- name: ipv6 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + check_mode: true + register: ipv6_allow_check +- name: ipv6 allow + ufw: + rule: allow + port: 23 + to_ip: "::" + register: ipv6_allow +- name: ipv6 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: "::" + register: ipv6_allow_idem +- name: ipv6 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + check_mode: true + register: ipv6_allow_idem_check +- assert: + that: + - ipv6_allow_check is changed + - ipv6_allow is changed + - ipv6_allow_idem is not changed + - ipv6_allow_idem_check is not changed + +# ############################################ +- name: delete ipv6 allow (check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + check_mode: true + register: delete_ipv6_allow_check +- name: delete ipv6 allow + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + register: delete_ipv6_allow +- name: delete ipv6 allow (idempotency) + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + register: delete_ipv6_allow_idem +- name: delete ipv6 allow (idempotency, check mode) + ufw: + rule: allow + port: 23 + to_ip: "::" + delete: true + check_mode: true + register: delete_ipv6_allow_idem_check +- assert: + that: + - delete_ipv6_allow_check is changed + - delete_ipv6_allow is changed + - delete_ipv6_allow_idem is not changed + - delete_ipv6_allow_idem_check is not changed + +# ############################################ +- name: Reload ufw + ufw: + state: reloaded + register: reload +- name: Reload ufw (check mode) + ufw: + state: reloaded + check_mode: true + register: reload_check +- assert: + that: + - reload is changed + - reload_check is changed + +# ############################################ +- name: Disable (check mode) + ufw: + state: disabled + check_mode: true + register: disable_check +- name: Disable + ufw: + state: disabled + register: disable +- name: Disable (idempotency) + ufw: + state: disabled + register: disable_idem +- name: Disable (idempotency, check mode) + ufw: + state: disabled + check_mode: true + register: disable_idem_check +- assert: + that: + - disable_check is changed + - disable is changed + - disable_idem is not changed + - disable_idem_check is not changed + +# ############################################ +- name: Re-enable + ufw: + state: enabled +- name: Reset (check mode) + ufw: + state: reset + check_mode: true + register: reset_check +- pause: + # Should not be needed, but since ufw is ignoring --dry-run for reset + # (https://bugs.launchpad.net/ufw/+bug/1810082) we have to wait here as well. + seconds: 1 +- name: Reset + ufw: + state: reset + register: reset +- pause: + # ufw creates backups of the rule files with a timestamp; if reset is called + # twice in a row fast enough (so that both timestamps are taken in the same second), + # the second call will notice that the backup files are already there and fail. + # Waiting one second fixes this problem. + seconds: 1 +- name: Reset (idempotency) + ufw: + state: reset + register: reset_idem +- pause: + # Should not be needed, but since ufw is ignoring --dry-run for reset + # (https://bugs.launchpad.net/ufw/+bug/1810082) we have to wait here as well. + seconds: 1 +- name: Reset (idempotency, check mode) + ufw: + state: reset + check_mode: true + register: reset_idem_check +- assert: + that: + - reset_check is changed + - reset is changed + - reset_idem is changed + - reset_idem_check is changed diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/global-state.yml b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/global-state.yml new file mode 100644 index 000000000..f5f100751 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/global-state.yml @@ -0,0 +1,154 @@ +--- +# 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: Enable ufw + ufw: + state: enabled + +# ############################################ +- name: Make sure logging is off + ufw: + logging: false +- name: Logging (check mode) + ufw: + logging: true + check_mode: true + register: logging_check +- name: Logging + ufw: + logging: true + register: logging +- name: Get logging + shell: | + ufw status verbose | grep "^Logging:" + register: ufw_logging + environment: + LC_ALL: C +- name: Logging (idempotency) + ufw: + logging: true + register: logging_idem +- name: Logging (idempotency, check mode) + ufw: + logging: true + check_mode: true + register: logging_idem_check +- name: Logging (change, check mode) + ufw: + logging: full + check_mode: true + register: logging_change_check +- name: Logging (change) + ufw: + logging: full + register: logging_change +- name: Get logging + shell: | + ufw status verbose | grep "^Logging:" + register: ufw_logging_change + environment: + LC_ALL: C +- assert: + that: + - logging_check is changed + - logging is changed + - "ufw_logging.stdout == 'Logging: on (low)'" + - logging_idem is not changed + - logging_idem_check is not changed + - "ufw_logging_change.stdout == 'Logging: on (full)'" + - logging_change is changed + - logging_change_check is changed + +# ############################################ +- name: Default (check mode) + ufw: + default: reject + direction: incoming + check_mode: true + register: default_check +- name: Default + ufw: + default: reject + direction: incoming + register: default +- name: Get defaults + shell: | + ufw status verbose | grep "^Default:" + register: ufw_defaults + environment: + LC_ALL: C +- name: Default (idempotency) + ufw: + default: reject + direction: incoming + register: default_idem +- name: Default (idempotency, check mode) + ufw: + default: reject + direction: incoming + check_mode: true + register: default_idem_check +- name: Default (change, check mode) + ufw: + default: allow + direction: incoming + check_mode: true + register: default_change_check +- name: Default (change) + ufw: + default: allow + direction: incoming + register: default_change +- name: Get defaults + shell: | + ufw status verbose | grep "^Default:" + register: ufw_defaults_change + environment: + LC_ALL: C +- name: Default (change again) + ufw: + default: deny + direction: incoming + register: default_change_2 +- name: Default (change incoming implicitly, check mode) + ufw: + default: allow + check_mode: true + register: default_change_implicit_check +- name: Default (change incoming implicitly) + ufw: + default: allow + register: default_change_implicit +- name: Get defaults + shell: | + ufw status verbose | grep "^Default:" + register: ufw_defaults_change_implicit + environment: + LC_ALL: C +- name: Default (change incoming implicitly, idempotent, check mode) + ufw: + default: allow + check_mode: true + register: default_change_implicit_idem_check +- name: Default (change incoming implicitly, idempotent) + ufw: + default: allow + register: default_change_implicit_idem +- assert: + that: + - default_check is changed + - default is changed + - "'reject (incoming)' in ufw_defaults.stdout" + - default_idem is not changed + - default_idem_check is not changed + - default_change_check is changed + - default_change is changed + - "'allow (incoming)' in ufw_defaults_change.stdout" + - default_change_2 is changed + - default_change_implicit_check is changed + - default_change_implicit is changed + - default_change_implicit_idem_check is not changed + - default_change_implicit_idem is not changed + - "'allow (incoming)' in ufw_defaults_change_implicit.stdout" diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/insert_relative_to.yml b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/insert_relative_to.yml new file mode 100644 index 000000000..67328a0e3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/insert_relative_to.yml @@ -0,0 +1,84 @@ +--- +# 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: Enable + ufw: + state: enabled + register: enable + +# ## CREATE RULES ############################ +- name: ipv4 + ufw: + rule: deny + port: 22 + to_ip: 0.0.0.0 +- name: ipv4 + ufw: + rule: deny + port: 23 + to_ip: 0.0.0.0 + +- name: ipv6 + ufw: + rule: deny + port: 122 + to_ip: "::" +- name: ipv6 + ufw: + rule: deny + port: 123 + to_ip: "::" + +- name: first-ipv4 + ufw: + rule: deny + port: 10 + to_ip: 0.0.0.0 + insert: 0 + insert_relative_to: first-ipv4 +- name: last-ipv4 + ufw: + rule: deny + port: 11 + to_ip: 0.0.0.0 + insert: 0 + insert_relative_to: last-ipv4 + +- name: first-ipv6 + ufw: + rule: deny + port: 110 + to_ip: "::" + insert: 0 + insert_relative_to: first-ipv6 +- name: last-ipv6 + ufw: + rule: deny + port: 111 + to_ip: "::" + insert: 0 + insert_relative_to: last-ipv6 + +# ## CHECK RESULT ############################ +- name: Get rules + shell: | + ufw status | grep DENY | cut -f 1-2 -d ' ' | grep -E "^(0\.0\.0\.0|::) [123]+" + # Note that there was also a rule "ff02::fb mDNS" on at least one CI run; + # to ignore these, the extra filtering (grepping for DENY and the regex) makes + # sure to remove all rules not added here. + register: ufw_status +- assert: + that: + - ufw_status.stdout_lines == expected_stdout + vars: + expected_stdout: + - "0.0.0.0 10" + - "0.0.0.0 22" + - "0.0.0.0 11" + - "0.0.0.0 23" + - ":: 110" + - ":: 122" + - ":: 111" + - ":: 123" diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/interface.yml b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/interface.yml new file mode 100644 index 000000000..1ec3568aa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/ufw/tasks/tests/interface.yml @@ -0,0 +1,86 @@ +--- +# 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: Enable + ufw: + state: enabled + +- name: Route with interface in and out + ufw: + rule: allow + route: true + interface_in: foo + interface_out: bar + proto: tcp + from_ip: 1.1.1.1 + to_ip: 8.8.8.8 + from_port: 1111 + to_port: 2222 + +- name: Route with interface in + ufw: + rule: allow + route: true + interface_in: foo + proto: tcp + from_ip: 1.1.1.1 + from_port: 1111 + +- name: Route with interface out + ufw: + rule: allow + route: true + interface_out: bar + proto: tcp + from_ip: 1.1.1.1 + from_port: 1111 + +- name: Non-route with interface in + ufw: + rule: allow + interface_in: foo + proto: tcp + from_ip: 1.1.1.1 + from_port: 3333 + +- name: Non-route with interface out + ufw: + rule: allow + interface_out: bar + proto: tcp + from_ip: 1.1.1.1 + from_port: 4444 + +- name: Check result + shell: ufw status |grep -E '(ALLOW|DENY|REJECT|LIMIT)' |sed -E 's/[ \t]+/ /g' + register: ufw_status + +- assert: + that: + - '"8.8.8.8 2222/tcp on bar ALLOW FWD 1.1.1.1 1111/tcp on foo " in stdout' + - '"Anywhere ALLOW FWD 1.1.1.1 1111/tcp on foo " in stdout' + - '"Anywhere on bar ALLOW FWD 1.1.1.1 1111/tcp " in stdout' + - '"Anywhere on foo ALLOW 1.1.1.1 3333/tcp " in stdout' + - '"Anywhere ALLOW OUT 1.1.1.1 4444/tcp on bar " in stdout' + vars: + stdout: '{{ ufw_status.stdout_lines }}' + +- name: Non-route with interface_in and interface_out + ufw: + rule: allow + interface_in: foo + interface_out: bar + proto: tcp + from_ip: 1.1.1.1 + from_port: 1111 + to_ip: 8.8.8.8 + to_port: 2222 + ignore_errors: true + register: ufw_non_route_iface + +- assert: + that: + - ufw_non_route_iface is failed + - '"Only route rules" in ufw_non_route_iface.msg' diff --git a/ansible_collections/community/general/tests/integration/targets/wakeonlan/aliases b/ansible_collections/community/general/tests/integration/targets/wakeonlan/aliases new file mode 100644 index 000000000..dadd9f37a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/wakeonlan/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/2 +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/wakeonlan/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/wakeonlan/tasks/main.yml new file mode 100644 index 000000000..059748031 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/wakeonlan/tasks/main.yml @@ -0,0 +1,58 @@ +--- +#################################################################### +# 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: Send a magic Wake-on-LAN packet to 00:00:5E:00:53:66 + wakeonlan: + mac: 00:00:5E:00:53:66 + broadcast: 192.0.2.255 + +- name: Send a magic Wake-on-LAN packet on port 9 to 00-00-5E-00-53-66 + wakeonlan: + mac: 00-00-5E-00-53-66 + port: 9 + +- name: Provide an incorrect MAC length + wakeonlan: + mac: 00-00-5E-00-53-66-AB + port: 9 + ignore_errors: true + register: incorrect_mac_length + +- name: Check error message + assert: + that: + - incorrect_mac_length is failed + - incorrect_mac_length.msg is search('Incorrect MAC address length') + +- name: Provide an incorrect MAC format + wakeonlan: + mac: ZW-YX-WV-UT-SR-QP + port: 9 + ignore_errors: true + register: incorrect_mac_format + +- name: Check error message + assert: + that: + - incorrect_mac_format is failed + - incorrect_mac_format.msg is search('Incorrect MAC address format') + +- name: Cause a socket error + wakeonlan: + mac: 00-00-5E-00-53-66 + broadcast: 345.567.678.890 + ignore_errors: true + register: incorrect_broadcast_address + +- name: Check error message + assert: + that: + - incorrect_broadcast_address is failed + - incorrect_broadcast_address.msg is search('not known|Name does not resolve') diff --git a/ansible_collections/community/general/tests/integration/targets/xattr/aliases b/ansible_collections/community/general/tests/integration/targets/xattr/aliases new file mode 100644 index 000000000..5cd9c012e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xattr/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/2 +azp/posix/vm +skip/aix +skip/docker +skip/freebsd +skip/osx +skip/macos +destructive diff --git a/ansible_collections/community/general/tests/integration/targets/xattr/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/xattr/defaults/main.yml new file mode 100644 index 000000000..29c6d5d15 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xattr/defaults/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 + +test_file: "{{ remote_tmp_dir }}/foo.txt" diff --git a/ansible_collections/community/general/tests/integration/targets/xattr/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/xattr/meta/main.yml new file mode 100644 index 000000000..ca1915e05 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xattr/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/xattr/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/xattr/tasks/main.yml new file mode 100644 index 000000000..6c1c02b3e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xattr/tasks/main.yml @@ -0,0 +1,21 @@ +--- +#################################################################### +# 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: Setup + include_tasks: setup.yml + +- name: Check availability of xattr support + command: setfattr -n user.foo {{ test_file }} + ignore_errors: true + register: xattr + +- name: Test + include_tasks: test.yml + when: xattr is not failed diff --git a/ansible_collections/community/general/tests/integration/targets/xattr/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/xattr/tasks/setup.yml new file mode 100644 index 000000000..0eda72d8c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xattr/tasks/setup.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: Install + package: + name: attr + state: present + +- name: Create file + file: + path: "{{ test_file }}" + state: touch diff --git a/ansible_collections/community/general/tests/integration/targets/xattr/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/xattr/tasks/test.yml new file mode 100644 index 000000000..7fe852d77 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xattr/tasks/test.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: Set attributes + xattr: + path: "{{ test_file }}" + key: user.foo + value: bar + register: xattr_set_result + +- name: Get attributes + xattr: + path: "{{ test_file }}" + register: xattr_get_all_result + +- name: Get specific attribute + xattr: + path: "{{ test_file }}" + key: foo + register: xattr_get_specific_result + +- assert: + that: + - "xattr_set_result.changed" + - "xattr_get_all_result['xattr']['user.foo'] == 'bar'" + - "not xattr_get_all_result.changed" + - "xattr_get_specific_result['xattr']['user.foo'] == 'bar'" + - "not xattr_get_specific_result.changed" + +- name: Set attribute again + xattr: + path: "{{ test_file }}" + namespace: user + key: foo + value: bar + register: xattr_set_again_result + +- assert: + that: + - "not xattr_set_again_result.changed" + +- name: Unset attribute + xattr: + path: "{{ test_file }}" + key: foo + state: absent + register: xattr_unset_result + +- name: Get attributes + xattr: + path: "{{ test_file }}" + register: xattr_get_after_unset_result + +- assert: + that: + - "xattr_unset_result.changed" + - "xattr_get_after_unset_result['xattr'] == {}" + - "not xattr_get_after_unset_result.changed" + +- name: Unset attribute again + xattr: + path: "{{ test_file }}" + namespace: user + key: foo + state: absent + register: xattr_unset_result + +- assert: + that: + - "not xattr_set_again_result.changed" diff --git a/ansible_collections/community/general/tests/integration/targets/xfs_quota/aliases b/ansible_collections/community/general/tests/integration/targets/xfs_quota/aliases new file mode 100644 index 000000000..d9f5f0fa3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/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 +needs/privileged +needs/root +skip/aix +skip/osx +skip/macos +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/xfs_quota/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/xfs_quota/defaults/main.yml new file mode 100644 index 000000000..b209f949a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/defaults/main.yml @@ -0,0 +1,46 @@ +--- +# 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 + +uquota_default_bsoft: 1m +uquota_default_bhard: 2m +uquota_default_isoft: 100 +uquota_default_ihard: 200 +uquota_default_rtbsoft: 1m +uquota_default_rtbhard: 2m + +uquota_user_bsoft: 2m +uquota_user_bhard: 3m +uquota_user_isoft: 300 +uquota_user_ihard: 400 +uquota_user_rtbsoft: 3m +uquota_user_rtbhard: 4m + +gquota_default_bsoft: 1m +gquota_default_bhard: 2m +gquota_default_isoft: 100 +gquota_default_ihard: 200 +gquota_default_rtbsoft: 1m +gquota_default_rtbhard: 2m + +gquota_group_bsoft: 2m +gquota_group_bhard: 3m +gquota_group_isoft: 300 +gquota_group_ihard: 400 +gquota_group_rtbsoft: 3m +gquota_group_rtbhard: 4m + +pquota_default_bsoft: 1m +pquota_default_bhard: 2m +pquota_default_isoft: 100 +pquota_default_ihard: 200 +pquota_default_rtbsoft: 1m +pquota_default_rtbhard: 2m + +pquota_project_bsoft: 2m +pquota_project_bhard: 3m +pquota_project_isoft: 300 +pquota_project_ihard: 400 +pquota_project_rtbsoft: 3m +pquota_project_rtbhard: 4m diff --git a/ansible_collections/community/general/tests/integration/targets/xfs_quota/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/xfs_quota/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/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/xfs_quota/tasks/gquota.yml b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/gquota.yml new file mode 100644 index 000000000..caca1d341 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/gquota.yml @@ -0,0 +1,147 @@ +--- +# 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 disk image + command: 'dd if=/dev/zero of={{ remote_tmp_dir }}/img-gquota bs=1M count=400 + + ' +- name: Create XFS filesystem + filesystem: + dev: '{{ remote_tmp_dir }}/img-gquota' + fstype: xfs +- block: + - name: Mount filesystem + become: true + ansible.posix.mount: + fstab: '{{ remote_tmp_dir }}/fstab' + src: '{{ remote_tmp_dir }}/img-gquota' + path: '{{ remote_tmp_dir }}/gquota' + fstype: xfs + opts: gquota + state: mounted + - name: Apply default group limits + xfs_quota: + bsoft: '{{ gquota_default_bsoft }}' + bhard: '{{ gquota_default_bhard }}' + isoft: '{{ gquota_default_isoft }}' + ihard: '{{ gquota_default_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/gquota' + rtbsoft: '{{ gquota_default_rtbsoft }}' + rtbhard: '{{ gquota_default_rtbhard }}' + type: group + become: true + register: test_gquota_default_before + - name: Assert default group limits results + assert: + that: + - test_gquota_default_before.changed + - test_gquota_default_before.bsoft == gquota_default_bsoft|human_to_bytes + - test_gquota_default_before.bhard == gquota_default_bhard|human_to_bytes + - test_gquota_default_before.isoft == gquota_default_isoft + - test_gquota_default_before.ihard == gquota_default_ihard + - test_gquota_default_before.rtbsoft == gquota_default_rtbsoft|human_to_bytes + - test_gquota_default_before.rtbhard == gquota_default_rtbhard|human_to_bytes + - name: Apply group limits + xfs_quota: + bsoft: '{{ gquota_group_bsoft }}' + bhard: '{{ gquota_group_bhard }}' + isoft: '{{ gquota_group_isoft }}' + ihard: '{{ gquota_group_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/gquota' + name: xfsquotauser + rtbsoft: '{{ gquota_group_rtbsoft }}' + rtbhard: '{{ gquota_group_rtbhard }}' + type: group + become: true + register: test_gquota_group_before + - name: Assert group limits results for xfsquotauser + assert: + that: + - test_gquota_group_before.changed + - test_gquota_group_before.bsoft == gquota_group_bsoft|human_to_bytes + - test_gquota_group_before.bhard == gquota_group_bhard|human_to_bytes + - test_gquota_group_before.isoft == gquota_group_isoft + - test_gquota_group_before.ihard == gquota_group_ihard + - test_gquota_group_before.rtbsoft == gquota_group_rtbsoft|human_to_bytes + - test_gquota_group_before.rtbhard == gquota_group_rtbhard|human_to_bytes + - name: Re-apply default group limits + xfs_quota: + bsoft: '{{ gquota_default_bsoft }}' + bhard: '{{ gquota_default_bhard }}' + isoft: '{{ gquota_default_isoft }}' + ihard: '{{ gquota_default_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/gquota' + rtbsoft: '{{ gquota_default_rtbsoft }}' + rtbhard: '{{ gquota_default_rtbhard }}' + type: group + become: true + register: test_gquota_default_after + - name: Assert default group limits results after re-apply + assert: + that: + - not test_gquota_default_after.changed + - name: Re-apply group limits + xfs_quota: + bsoft: '{{ gquota_group_bsoft }}' + bhard: '{{ gquota_group_bhard }}' + isoft: '{{ gquota_group_isoft }}' + ihard: '{{ gquota_group_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/gquota' + name: xfsquotauser + rtbsoft: '{{ gquota_group_rtbsoft }}' + rtbhard: '{{ gquota_group_rtbhard }}' + type: group + become: true + register: test_gquota_group_after + - name: Assert group limits results for xfsquotauser after re-apply + assert: + that: + - not test_gquota_group_after.changed + - name: Reset default group limits + xfs_quota: + mountpoint: '{{ remote_tmp_dir }}/gquota' + state: absent + type: group + become: true + register: test_reset_gquota_default + - name: Assert reset of default group limits results + assert: + that: + - test_reset_gquota_default.changed + - test_reset_gquota_default.bsoft == 0 + - test_reset_gquota_default.bhard == 0 + - test_reset_gquota_default.isoft == 0 + - test_reset_gquota_default.ihard == 0 + - test_reset_gquota_default.rtbsoft == 0 + - test_reset_gquota_default.rtbhard == 0 + - name: Reset group limits for xfsquotauser + xfs_quota: + mountpoint: '{{ remote_tmp_dir }}/gquota' + name: xfsquotauser + state: absent + type: group + become: true + register: test_reset_gquota_group + - name: Assert reset of default group limits results + assert: + that: + - test_reset_gquota_group.changed + - test_reset_gquota_group.bsoft == 0 + - test_reset_gquota_group.bhard == 0 + - test_reset_gquota_group.isoft == 0 + - test_reset_gquota_group.ihard == 0 + - test_reset_gquota_group.rtbsoft == 0 + - test_reset_gquota_group.rtbhard == 0 + always: + - name: Unmount filesystem + become: true + ansible.posix.mount: + fstab: '{{ remote_tmp_dir }}/fstab' + path: '{{ remote_tmp_dir }}/gquota' + state: unmounted + - name: Remove disk image + file: + path: '{{ remote_tmp_dir }}/img-gquota' + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/main.yml new file mode 100644 index 000000000..8977cf9ec --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/main.yml @@ -0,0 +1,37 @@ +--- +#################################################################### +# 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: setup Alpine + when: ansible_distribution == 'Alpine' + package: + name: + - xfsprogs + - xfsprogs-extra + - mount + - umount + state: latest + +- block: + - name: Create test user + user: + name: xfsquotauser + state: present + become: true + + - include_tasks: uquota.yml + - include_tasks: gquota.yml + - include_tasks: pquota.yml + + always: + - name: cleanup test user + user: + name: xfsquotauser + state: absent + become: true diff --git a/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/pquota.yml b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/pquota.yml new file mode 100644 index 000000000..db364ffd5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/pquota.yml @@ -0,0 +1,184 @@ +--- +# 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 disk image + command: 'dd if=/dev/zero of={{ remote_tmp_dir }}/img-pquota bs=1M count=400 + + ' +- name: Create XFS filesystem + filesystem: + dev: '{{ remote_tmp_dir }}/img-pquota' + fstype: xfs +- name: Create xfs related files + file: + path: /etc/{{ item }} + state: touch + become: true + loop: + - projid + - projects +- name: Add test xfs quota project id + lineinfile: + path: /etc/projid + line: xft_quotaval:99999 + state: present + become: true +- name: Add test xfs quota project path + lineinfile: + path: /etc/projects + line: 99999:{{ remote_tmp_dir }}/pquota/test + state: present + become: true +- block: + - name: Mount filesystem + become: true + ansible.posix.mount: + fstab: '{{ remote_tmp_dir }}/fstab' + src: '{{ remote_tmp_dir }}/img-pquota' + path: '{{ remote_tmp_dir }}/pquota' + fstype: xfs + opts: pquota + state: mounted + - name: Create test directory + file: + path: '{{ remote_tmp_dir }}/pquota/test' + state: directory + become: true + - name: Apply default project limits + xfs_quota: + bsoft: '{{ pquota_default_bsoft }}' + bhard: '{{ pquota_default_bhard }}' + isoft: '{{ pquota_default_isoft }}' + ihard: '{{ pquota_default_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/pquota' + rtbsoft: '{{ pquota_default_rtbsoft }}' + rtbhard: '{{ pquota_default_rtbhard }}' + type: project + become: true + register: test_pquota_default_before + - name: Assert default project limits results + assert: + that: + - test_pquota_default_before.changed + - test_pquota_default_before.bsoft == pquota_default_bsoft|human_to_bytes + - test_pquota_default_before.bhard == pquota_default_bhard|human_to_bytes + - test_pquota_default_before.isoft == pquota_default_isoft + - test_pquota_default_before.ihard == pquota_default_ihard + - test_pquota_default_before.rtbsoft == pquota_default_rtbsoft|human_to_bytes + - test_pquota_default_before.rtbhard == pquota_default_rtbhard|human_to_bytes + - name: Apply project limits + xfs_quota: + bsoft: '{{ pquota_project_bsoft }}' + bhard: '{{ pquota_project_bhard }}' + isoft: '{{ pquota_project_isoft }}' + ihard: '{{ pquota_project_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/pquota' + name: xft_quotaval + rtbsoft: '{{ pquota_project_rtbsoft }}' + rtbhard: '{{ pquota_project_rtbhard }}' + type: project + become: true + register: test_pquota_project_before + - name: Assert project limits results for xft_quotaval + assert: + that: + - test_pquota_project_before.changed + - test_pquota_project_before.bsoft == pquota_project_bsoft|human_to_bytes + - test_pquota_project_before.bhard == pquota_project_bhard|human_to_bytes + - test_pquota_project_before.isoft == pquota_project_isoft + - test_pquota_project_before.ihard == pquota_project_ihard + - test_pquota_project_before.rtbsoft == pquota_project_rtbsoft|human_to_bytes + - test_pquota_project_before.rtbhard == pquota_project_rtbhard|human_to_bytes + - name: Re-apply default project limits + xfs_quota: + bsoft: '{{ pquota_default_bsoft }}' + bhard: '{{ pquota_default_bhard }}' + isoft: '{{ pquota_default_isoft }}' + ihard: '{{ pquota_default_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/pquota' + rtbsoft: '{{ pquota_default_rtbsoft }}' + rtbhard: '{{ pquota_default_rtbhard }}' + type: project + become: true + register: test_pquota_default_after + - name: Assert default project limits results after re-apply + assert: + that: + - not test_pquota_default_after.changed + - name: Re-apply project limits + xfs_quota: + bsoft: '{{ pquota_project_bsoft }}' + bhard: '{{ pquota_project_bhard }}' + isoft: '{{ pquota_project_isoft }}' + ihard: '{{ pquota_project_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/pquota' + name: xft_quotaval + rtbsoft: '{{ pquota_project_rtbsoft }}' + rtbhard: '{{ pquota_project_rtbhard }}' + type: project + become: true + register: test_pquota_project_after + - name: Assert project limits results for xft_quotaval after re-apply + assert: + that: + - test_pquota_project_after is not changed + - name: Reset default project limits + xfs_quota: + mountpoint: '{{ remote_tmp_dir }}/pquota' + state: absent + type: project + become: true + register: test_reset_pquota_default + - name: Assert reset of default projecy limits results + assert: + that: + - test_reset_pquota_default.changed + - test_reset_pquota_default.bsoft == 0 + - test_reset_pquota_default.bhard == 0 + - test_reset_pquota_default.isoft == 0 + - test_reset_pquota_default.ihard == 0 + - test_reset_pquota_default.rtbsoft == 0 + - test_reset_pquota_default.rtbhard == 0 + - name: Reset project limits for xft_quotaval + xfs_quota: + mountpoint: '{{ remote_tmp_dir }}/pquota' + name: xft_quotaval + state: absent + type: project + become: true + register: test_reset_pquota_project + - name: Assert reset of project limits results for xft_quotaval + assert: + that: + - test_reset_pquota_project.changed + - test_reset_pquota_project.bsoft == 0 + - test_reset_pquota_project.bhard == 0 + - test_reset_pquota_project.isoft == 0 + - test_reset_pquota_project.ihard == 0 + - test_reset_pquota_project.rtbsoft == 0 + - test_reset_pquota_project.rtbhard == 0 + always: + - name: Unmount filesystem + become: true + ansible.posix.mount: + fstab: '{{ remote_tmp_dir }}/fstab' + path: '{{ remote_tmp_dir }}/pquota' + state: unmounted + - name: Remove disk image + file: + path: '{{ remote_tmp_dir }}/img-pquota' + state: absent + - name: Remove xfs quota project id + lineinfile: + path: /etc/projid + regexp: ^xft_quotaval:99999$ + state: absent + become: true + - name: Remove xfs quota project path + lineinfile: + path: /etc/projects + regexp: ^99999:.*$ + state: absent + become: true diff --git a/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/uquota.yml b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/uquota.yml new file mode 100644 index 000000000..36a7eff76 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xfs_quota/tasks/uquota.yml @@ -0,0 +1,147 @@ +--- +# 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 disk image + command: 'dd if=/dev/zero of={{ remote_tmp_dir }}/img-uquota bs=1M count=400 + + ' +- name: Create XFS filesystem + filesystem: + dev: '{{ remote_tmp_dir }}/img-uquota' + fstype: xfs +- block: + - name: Mount filesystem + become: true + ansible.posix.mount: + fstab: '{{ remote_tmp_dir }}/fstab' + src: '{{ remote_tmp_dir }}/img-uquota' + path: '{{ remote_tmp_dir }}/uquota' + fstype: xfs + opts: uquota + state: mounted + - name: Apply default user limits + xfs_quota: + bsoft: '{{ uquota_default_bsoft }}' + bhard: '{{ uquota_default_bhard }}' + isoft: '{{ uquota_default_isoft }}' + ihard: '{{ uquota_default_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/uquota' + rtbsoft: '{{ uquota_default_rtbsoft }}' + rtbhard: '{{ uquota_default_rtbhard }}' + type: user + become: true + register: test_uquota_default_before + - name: Assert default user limits results + assert: + that: + - test_uquota_default_before.changed + - test_uquota_default_before.bsoft == uquota_default_bsoft|human_to_bytes + - test_uquota_default_before.bhard == uquota_default_bhard|human_to_bytes + - test_uquota_default_before.isoft == uquota_default_isoft + - test_uquota_default_before.ihard == uquota_default_ihard + - test_uquota_default_before.rtbsoft == uquota_default_rtbsoft|human_to_bytes + - test_uquota_default_before.rtbhard == uquota_default_rtbhard|human_to_bytes + - name: Apply user limits + xfs_quota: + bsoft: '{{ uquota_user_bsoft }}' + bhard: '{{ uquota_user_bhard }}' + isoft: '{{ uquota_user_isoft }}' + ihard: '{{ uquota_user_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/uquota' + name: xfsquotauser + rtbsoft: '{{ uquota_user_rtbsoft }}' + rtbhard: '{{ uquota_user_rtbhard }}' + type: user + become: true + register: test_uquota_user_before + - name: Assert user limits results + assert: + that: + - test_uquota_user_before.changed + - test_uquota_user_before.bsoft == uquota_user_bsoft|human_to_bytes + - test_uquota_user_before.bhard == uquota_user_bhard|human_to_bytes + - test_uquota_user_before.isoft == uquota_user_isoft + - test_uquota_user_before.ihard == uquota_user_ihard + - test_uquota_user_before.rtbsoft == uquota_user_rtbsoft|human_to_bytes + - test_uquota_user_before.rtbhard == uquota_user_rtbhard|human_to_bytes + - name: Re-apply default user limits + xfs_quota: + bsoft: '{{ uquota_default_bsoft }}' + bhard: '{{ uquota_default_bhard }}' + isoft: '{{ uquota_default_isoft }}' + ihard: '{{ uquota_default_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/uquota' + rtbsoft: '{{ uquota_default_rtbsoft }}' + rtbhard: '{{ uquota_default_rtbhard }}' + type: user + become: true + register: test_uquota_default_after + - name: Assert default user limits results after re-apply + assert: + that: + - not test_uquota_default_after.changed + - name: Re-apply user limits + xfs_quota: + bsoft: '{{ uquota_user_bsoft }}' + bhard: '{{ uquota_user_bhard }}' + isoft: '{{ uquota_user_isoft }}' + ihard: '{{ uquota_user_ihard }}' + mountpoint: '{{ remote_tmp_dir }}/uquota' + name: xfsquotauser + rtbsoft: '{{ uquota_user_rtbsoft }}' + rtbhard: '{{ uquota_user_rtbhard }}' + type: user + become: true + register: test_uquota_user_after + - name: Assert user limits results for xfsquotauser after re-apply + assert: + that: + - not test_uquota_user_after.changed + - name: Reset default user limits + xfs_quota: + mountpoint: '{{ remote_tmp_dir }}/uquota' + state: absent + type: user + become: true + register: test_reset_uquota_default + - name: Assert reset of default user limits results + assert: + that: + - test_reset_uquota_default.changed + - test_reset_uquota_default.bsoft == 0 + - test_reset_uquota_default.bhard == 0 + - test_reset_uquota_default.isoft == 0 + - test_reset_uquota_default.ihard == 0 + - test_reset_uquota_default.rtbsoft == 0 + - test_reset_uquota_default.rtbhard == 0 + - name: Reset user limits for xfsquotauser + xfs_quota: + mountpoint: '{{ remote_tmp_dir }}/uquota' + name: xfsquotauser + state: absent + type: user + become: true + register: test_reset_uquota_user + - name: Assert reset of default user limits results + assert: + that: + - test_reset_uquota_user.changed + - test_reset_uquota_user.bsoft == 0 + - test_reset_uquota_user.bhard == 0 + - test_reset_uquota_user.isoft == 0 + - test_reset_uquota_user.ihard == 0 + - test_reset_uquota_user.rtbsoft == 0 + - test_reset_uquota_user.rtbhard == 0 + always: + - name: Unmount filesystem + become: true + ansible.posix.mount: + fstab: '{{ remote_tmp_dir }}/fstab' + path: '{{ remote_tmp_dir }}/uquota' + state: unmounted + - name: Remove disk image + file: + path: '{{ remote_tmp_dir }}/img-uquota' + state: absent diff --git a/ansible_collections/community/general/tests/integration/targets/xml/aliases b/ansible_collections/community/general/tests/integration/targets/xml/aliases new file mode 100644 index 000000000..0d1324b22 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/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 +destructive +skip/aix diff --git a/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml new file mode 100644 index 000000000..d0e3e39af --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml @@ -0,0 +1,13 @@ + + + Толстый бар + + Окское + Невское + + десять + + +
http://tolstyybar.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers-unicode.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml new file mode 100644 index 000000000..f47909ac6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-beers.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml new file mode 100644 index 000000000..acaca7f59 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/fixtures/ansible-xml-namespaced-beers.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/xml/meta/main.yml new file mode 100644 index 000000000..2fcd152f9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/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/xml/results/test-add-children-elements-unicode.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml new file mode 100644 index 000000000..ebf02ecf5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + Окское + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements-unicode.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml new file mode 100644 index 000000000..3fff3d0d2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + Old Rasputin + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-elements.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml new file mode 100644 index 000000000..e9b59a6ac --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + Natty LiteMiller LiteCoors Lite + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-from-groupvars.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml new file mode 100644 index 000000000..8da963363 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml @@ -0,0 +1,17 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Old Rasputin + Old Motor Oil + Old Curmudgeon + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertafter.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml new file mode 100644 index 000000000..c409e54bf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml @@ -0,0 +1,17 @@ + + + Tasty Beverage Co. + + Rochefort 10 + Old Rasputin + Old Motor Oil + Old Curmudgeon + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-insertbefore.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml new file mode 100644 index 000000000..f206af231 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes-unicode.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml new file mode 100644 index 000000000..a4471b7f1 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-children-with-attributes.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml new file mode 100644 index 000000000..fa1ddfca2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml @@ -0,0 +1,32 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + George Killian's Irish Red + Pilsner Urquell + + 10 + + +
http://tastybeverageco.com
+ +
+ 555-555-1234 + + + Smith + John + Q + + + + + + xml tag with no special characters + xml tag with dashes + xml tag with dashes and dots + xml tag with dashes, dots and underscores +
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-element-implicitly.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml new file mode 100644 index 000000000..dc53d2f8a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + Old Rasputin + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-add-namespaced-children-elements.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml new file mode 100644 index 000000000..f47909ac6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print-only.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml new file mode 100644 index 000000000..b5c38262f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml @@ -0,0 +1,15 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + Old Rasputin + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-pretty-print.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml new file mode 100644 index 000000000..579741918 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-attribute.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml new file mode 100644 index 000000000..b7b1a1a5c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml @@ -0,0 +1,13 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-element.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml new file mode 100644 index 000000000..4c4dcb180 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-attribute.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml new file mode 100644 index 000000000..e72312032 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml @@ -0,0 +1,13 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-remove-namespaced-element.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml new file mode 100644 index 000000000..df50daba4 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value-unicode.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml new file mode 100644 index 000000000..28dcff81a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-attribute-value.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml new file mode 100644 index 000000000..51eb98f16 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml @@ -0,0 +1,11 @@ + + + Tasty Beverage Co. + + + 10 + + +
http://tastybeverageco.com
+
+
\ No newline at end of file diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-empty-list.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml new file mode 100644 index 000000000..b985090d7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml @@ -0,0 +1,11 @@ + + + Tasty Beverage Co. + + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-level.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml new file mode 100644 index 000000000..3cd586dd0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml @@ -0,0 +1,11 @@ + + + Tasty Beverage Co. + + ОкскоеНевское + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements-unicode.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml new file mode 100644 index 000000000..6348c3e8f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml @@ -0,0 +1,11 @@ + + + Tasty Beverage Co. + + 90 Minute IPAHarvest Pumpkin Ale + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-children-elements.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml new file mode 100644 index 000000000..5ecab798e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-empty.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml new file mode 100644 index 000000000..6c5ca8dd9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + пять + + +
http://tastybeverageco.com
+
+пять
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value-unicode.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml new file mode 100644 index 000000000..59fb1e516 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 5 + + +
http://tastybeverageco.com
+
+5
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-element-value.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml new file mode 100644 index 000000000..229b31ad7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 10 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-attribute-value.xml.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml new file mode 100644 index 000000000..f78873e7a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml @@ -0,0 +1,14 @@ + + + Tasty Beverage Co. + + Rochefort 10 + St. Bernardus Abbot 12 + Schlitz + + 11 + + +
http://tastybeverageco.com
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml.license b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/results/test-set-namespaced-element-value.xml.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: Ansible Project 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 new file mode 100644 index 000000000..fe46b3ae5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml @@ -0,0 +1,77 @@ +--- +#################################################################### +# 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 lxml (FreeBSD) + package: + name: 'py{{ ansible_python.version.major }}{{ ansible_python.version.minor }}-lxml' + state: present + when: ansible_os_family == "FreeBSD" + +# Needed for MacOSX ! +- name: Install lxml + pip: + name: lxml + state: present +# when: ansible_os_family == "Darwin" + +- name: Get lxml version + command: "{{ ansible_python_interpreter }} -c 'from lxml import etree; print(\".\".join(str(v) for v in etree.LXML_VERSION))'" + register: lxml_version + +- name: Set lxml capabilities as variables + set_fact: + # NOTE: Some tests require predictable element attribute order, + # which is only guaranteed starting from lxml v3.0alpha1 + lxml_predictable_attribute_order: '{{ lxml_version.stdout is version("3", ">=") }}' + + # NOTE: The xml module requires at least lxml v2.3.0 + lxml_xpath_attribute_result_attrname: '{{ lxml_version.stdout is version("2.3.0", ">=") }}' + +- name: Only run the tests when lxml v2.3.0+ + when: lxml_xpath_attribute_result_attrname + block: + + - include_tasks: test-add-children-elements.yml + - include_tasks: test-add-children-from-groupvars.yml + - include_tasks: test-add-children-insertafter.yml + - include_tasks: test-add-children-insertbefore.yml + - include_tasks: test-add-children-with-attributes.yml + - include_tasks: test-add-element-implicitly.yml + - include_tasks: test-count.yml + - include_tasks: test-mutually-exclusive-attributes.yml + - include_tasks: test-remove-attribute.yml + - include_tasks: test-remove-attribute-nochange.yml + - include_tasks: test-remove-element.yml + - include_tasks: test-remove-element-nochange.yml + - include_tasks: test-set-attribute-value.yml + - include_tasks: test-set-children-elements.yml + - include_tasks: test-set-children-elements-level.yml + - include_tasks: test-set-element-value.yml + - include_tasks: test-set-element-value-empty.yml + - include_tasks: test-pretty-print.yml + - include_tasks: test-pretty-print-only.yml + - include_tasks: test-add-namespaced-children-elements.yml + - include_tasks: test-remove-namespaced-attribute.yml + - include_tasks: test-remove-namespaced-attribute-nochange.yml + - include_tasks: test-set-namespaced-attribute-value.yml + - include_tasks: test-set-namespaced-element-value.yml + - include_tasks: test-set-namespaced-children-elements.yml + - include_tasks: test-get-element-content.yml + - include_tasks: test-xmlstring.yml + - include_tasks: test-children-elements-xml.yml + + # Unicode tests + - include_tasks: test-add-children-elements-unicode.yml + - include_tasks: test-add-children-with-attributes-unicode.yml + - include_tasks: test-set-attribute-value-unicode.yml + - include_tasks: test-count-unicode.yml + - include_tasks: test-get-element-content.yml + - include_tasks: test-set-children-elements-unicode.yml + - include_tasks: test-set-element-value-unicode.yml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml new file mode 100644 index 000000000..e15ac5fd9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements-unicode.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + add_children: + - beer: Окское + register: add_children_elements_unicode + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-children-elements-unicode.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_children_elements_unicode is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-children-elements-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements.yml new file mode 100644 index 000000000..29467f6d6 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-elements.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + add_children: + - beer: Old Rasputin + register: add_children_elements + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-children-elements.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_children_elements is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml new file mode 100644 index 000000000..2b232b6d0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-from-groupvars.yml @@ -0,0 +1,35 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + add_children: '{{ bad_beers }}' + register: add_children_from_groupvars + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-children-from-groupvars.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_children_from_groupvars is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-children-from-groupvars.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml new file mode 100644 index 000000000..7795c8966 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertafter.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]' + insertafter: true + add_children: + - beer: Old Rasputin + - beer: Old Motor Oil + - beer: Old Curmudgeon + pretty_print: true + register: add_children_insertafter + + - name: Compare to expected result + copy: + src: results/test-add-children-insertafter.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_children_insertafter is changed + - comparison is not changed # identical diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml new file mode 100644 index 000000000..b14c5e06f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-insertbefore.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: '/business/beers/beer[text()="St. Bernardus Abbot 12"]' + insertbefore: true + add_children: + - beer: Old Rasputin + - beer: Old Motor Oil + - beer: Old Curmudgeon + pretty_print: true + register: add_children_insertbefore + + - name: Compare to expected result + copy: + src: results/test-add-children-insertbefore.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_children_insertbefore is changed + - comparison is not changed # identical diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.yml new file mode 100644 index 000000000..07905aa15 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes-unicode.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + add_children: + - beer: + name: Окское + type: экстра + register: add_children_with_attributes_unicode + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-children-with-attributes-unicode.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_children_with_attributes_unicode is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-children-with-attributes-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml new file mode 100644 index 000000000..fede24395 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-children-with-attributes.yml @@ -0,0 +1,42 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + add_children: + - beer: + name: Ansible Brew + type: light + register: add_children_with_attributes + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-children-with-attributes.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + # NOTE: This test may fail if lxml does not support predictable element attribute order + # So we filter the failure out for these platforms (e.g. CentOS 6) + # The module still works fine, we simply are not comparing as smart as we should. + - name: Test expected result + assert: + that: + - add_children_with_attributes is changed + - comparison is not changed # identical + when: lxml_predictable_attribute_order + #command: diff -u {{ role_path }}/results/test-add-children-with-attributes.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml new file mode 100644 index 000000000..b1718e452 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-element-implicitly.yml @@ -0,0 +1,241 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers-implicit.xml + + +- name: Add a phonenumber element to the business element. Implicit mkdir -p behavior where applicable + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/phonenumber + value: 555-555-1234 + +- name: Add a owner element to the business element, testing implicit mkdir -p behavior 1/2 + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/owner/name/last + value: Smith + +- name: Add a owner element to the business element, testing implicit mkdir -p behavior 2/2 + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/owner/name/first + value: John + +- name: Add a validxhtml element to the website element. Note that ensure is present by default and while value defaults to null for elements, if one doesn't specify it we don't know what to do. + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/website/validxhtml + +- name: Add an empty validateon attribute to the validxhtml element. This actually makes the previous example redundant because of the implicit parent-node creation behavior. + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/website/validxhtml/@validateon + +- name: Add an empty validateon attribute to the validxhtml element. Actually verifies the implicit parent-node creation behavior. + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/website_bis/validxhtml/@validateon + +- name: Add an attribute with a value + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/owner/@dob='1976-04-12' + +- name: Add an element with a value, alternate syntax + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/beers/beer/text()="George Killian's Irish Red" # note the quote within an XPath string thing + +- name: Add an element without special characters + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/testnormalelement + value: xml tag with no special characters + pretty_print: true + +- name: Add an element with dash + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/test-with-dash + value: xml tag with dashes + pretty_print: true + +- name: Add an element with dot + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/test-with-dash.and.dot + value: xml tag with dashes and dots + pretty_print: true + +- name: Add an element with underscore + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/test-with.dash_and.dot_and-underscores + value: xml tag with dashes, dots and underscores + pretty_print: true + +- name: Add an attribute on a conditional element + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/beers/beer[text()="George Killian's Irish Red"]/@color='red' + +- name: Add two attributes on a conditional element + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/beers/beer[text()="Pilsner Urquell" and @origin='CZ']/@color='blonde' + +- name: Add a owner element to the business element, testing implicit mkdir -p behavior 3/2 -- complex lookup + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/owner/name[first/text()='John']/middle + value: Q + +- name: Pretty Print this! + xml: + file: /tmp/ansible-xml-beers-implicit.xml + pretty_print: true + +- name: Compare to expected result + copy: + src: results/test-add-element-implicitly.xml + dest: /tmp/ansible-xml-beers-implicit.xml + check_mode: true + diff: true + register: comparison + +- name: Test expected result + assert: + that: + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-element-implicitly.xml /tmp/ansible-xml-beers-implicit.xml + + +# Now we repeat the same, just to ensure proper use of namespaces +- name: Add a phonenumber element to the business element. Implicit mkdir -p behavior where applicable + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:phonenumber + value: 555-555-1234 + namespaces: + a: http://example.com/some/namespace + +- name: Add a owner element to the business element, testing implicit mkdir -p behavior 1/2 + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:owner/a:name/a:last + value: Smith + namespaces: + a: http://example.com/some/namespace + +- name: Add a owner element to the business element, testing implicit mkdir -p behavior 2/2 + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:owner/a:name/a:first + value: John + namespaces: + a: http://example.com/some/namespace + +- name: Add a validxhtml element to the website element. Note that ensure is present by default and while value defaults to null for elements, if one doesn't specify it we don't know what to do. + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:website/a:validxhtml + namespaces: + a: http://example.com/some/namespace + +- name: Add an empty validateon attribute to the validxhtml element. This actually makes the previous example redundant because of the implicit parent-node creation behavior. + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:website/a:validxhtml/@a:validateon + namespaces: + a: http://example.com/some/namespace + +- name: Add an empty validateon attribute to the validxhtml element. Actually verifies the implicit parent-node creation behavior. + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:website_bis/a:validxhtml/@a:validateon + namespaces: + a: http://example.com/some/namespace + +- name: Add an attribute with a value + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:owner/@a:dob='1976-04-12' + namespaces: + a: http://example.com/some/namespace + +- name: Add an element with a value, alternate syntax + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:beers/a:beer/text()="George Killian's Irish Red" # note the quote within an XPath string thing + namespaces: + a: http://example.com/some/namespace + +- name: Add an attribute on a conditional element + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:beers/a:beer[text()="George Killian's Irish Red"]/@a:color='red' + namespaces: + a: http://example.com/some/namespace + +- name: Add two attributes on a conditional element + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:beers/a:beer[text()="Pilsner Urquell" and @a:origin='CZ']/@a:color='blonde' + namespaces: + a: http://example.com/some/namespace + +- name: Add a owner element to the business element, testing implicit mkdir -p behavior 3/2 -- complex lookup + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/a:owner/a:name[a:first/text()='John']/a:middle + value: Q + namespaces: + a: http://example.com/some/namespace + +- name: Add an element without special characters + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/testnormalelement + value: xml tag with no special characters + pretty_print: true + namespaces: + a: http://example.com/some/namespace + + +- name: Add an element with dash + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/test-with-dash + value: xml tag with dashes + pretty_print: true + namespaces: + a: http://example.com/some/namespace + +- name: Add an element with dot + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/test-with-dash.and.dot + value: xml tag with dashes and dots + pretty_print: true + namespaces: + a: http://example.com/some/namespace + +- name: Add an element with underscore + xml: + file: /tmp/ansible-xml-beers-implicit.xml + xpath: /business/test-with.dash_and.dot_and-underscores + value: xml tag with dashes, dots and underscores + pretty_print: true + namespaces: + a: http://example.com/some/namespace + +- name: Pretty Print this! + xml: + file: /tmp/ansible-xml-beers-implicit.xml + pretty_print: true diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.yml new file mode 100644 index 000000000..2a9daab78 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-add-namespaced-children-elements.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Add namespaced child element + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + xpath: /bus:business/ber:beers + namespaces: + bus: http://test.business + ber: http://test.beers + add_children: + - beer: Old Rasputin + register: add_namespaced_children_elements + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-namespaced-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-namespaced-children-elements.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - add_namespaced_children_elements is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-namespaced-children-elements.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-children-elements-xml.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-children-elements-xml.yml new file mode 100644 index 000000000..1c8c2b804 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-children-elements-xml.yml @@ -0,0 +1,37 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element with xml format + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + input_type: xml + add_children: + - 'Old Rasputin' + register: children_elements + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-add-children-elements.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - children_elements is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-add-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count-unicode.yml new file mode 100644 index 000000000..118e2986d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count-unicode.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: Setup test fixture + copy: + src: fixtures/ansible-xml-beers-unicode.xml + dest: /tmp/ansible-xml-beers-unicode.xml + + + - name: Count child element + xml: + path: /tmp/ansible-xml-beers-unicode.xml + xpath: /business/beers/beer + count: true + register: beers + + - name: Test expected result + assert: + that: + - beers is not changed + - beers.count == 2 diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count.yml new file mode 100644 index 000000000..79be9402f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-count.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: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add child element + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers/beer + count: true + register: beers + + - name: Test expected result + assert: + that: + - beers is not changed + - beers.count == 3 diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml new file mode 100644 index 000000000..475f962eb --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content-unicode.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers-unicode.xml + dest: /tmp/ansible-xml-beers-unicode.xml + + + - name: Get element attributes + xml: + path: /tmp/ansible-xml-beers-unicode.xml + xpath: /business/rating + content: attribute + register: get_element_attribute + + - name: Test expected result + assert: + that: + - get_element_attribute is not changed + - get_element_attribute.matches[0]['rating'] is defined and get_element_attribute.matches[0]['rating']['subjective'] == 'да' + + - name: Get element text + xml: + path: /tmp/ansible-xml-beers-unicode.xml + xpath: /business/rating + content: text + register: get_element_text + + - name: Test expected result + assert: + that: + - get_element_text is not changed + - get_element_text.matches[0]['rating'] == 'десять' diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content.yml new file mode 100644 index 000000000..c75bdb223 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-get-element-content.yml @@ -0,0 +1,51 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Get element attributes + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + content: attribute + register: get_element_attribute + + - name: Test expected result + assert: + that: + - get_element_attribute is not changed + - get_element_attribute.matches[0]['rating'] is defined + - get_element_attribute.matches[0]['rating']['subjective'] == 'true' + + - name: Get element attributes (should fail) + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + content: attribute + attribute: subjective + register: get_element_attribute_wrong + ignore_errors: true + + - name: Test expected result + assert: + that: + - get_element_attribute_wrong is failed + + - name: Get element text + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + content: text + register: get_element_text + + - name: Test expected result + assert: + that: + - get_element_text is not changed + - get_element_text.matches[0]['rating'] == '10' diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.yml new file mode 100644 index 000000000..33f129e2e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-mutually-exclusive-attributes.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Specify both children to add and a value + xml: + path: /tmp/ansible-xml-beers.xml + add_children: + - child01 + - child02 + value: conflict! + register: module_output + ignore_errors: true + + - name: Test expected result + assert: + that: + - module_output is not changed + - module_output is failed diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print-only.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print-only.yml new file mode 100644 index 000000000..03d3299aa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print-only.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml.orig + + - name: Remove spaces from test fixture + shell: sed 's/^[ ]*//g' < /tmp/ansible-xml-beers.xml.orig > /tmp/ansible-xml-beers.xml + + - name: Pretty print without modification + xml: + path: /tmp/ansible-xml-beers.xml + pretty_print: true + register: pretty_print_only + + - name: Compare to expected result + copy: + src: results/test-pretty-print-only.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - pretty_print_only is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print.yml new file mode 100644 index 000000000..51b34502d --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-pretty-print.yml @@ -0,0 +1,34 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Pretty print + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + pretty_print: true + add_children: + - beer: Old Rasputin + register: pretty_print + + - name: Compare to expected result + copy: + src: results/test-pretty-print.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - pretty_print is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml new file mode 100644 index 000000000..3222bd436 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute-nochange.yml @@ -0,0 +1,32 @@ +--- +# 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 test fixture + copy: + src: results/test-remove-attribute.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Remove non-existing '/business/rating/@subjective' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating/@subjective + state: absent + register: remove_attribute + + - name: Compare to expected result + copy: + src: results/test-remove-attribute.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_attribute is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-attribute.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute.yml new file mode 100644 index 000000000..e8952a655 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-attribute.yml @@ -0,0 +1,35 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Remove '/business/rating/@subjective' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating/@subjective + state: absent + register: remove_attribute + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-remove-attribute.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_attribute is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-attribute.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml new file mode 100644 index 000000000..c1312c5a7 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element-nochange.yml @@ -0,0 +1,32 @@ +--- +# 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 test fixture + copy: + src: results/test-remove-element.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Remove non-existing '/business/rating' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + state: absent + register: remove_element + + - name: Compare to expected result + copy: + src: results/test-remove-element.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_element is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element.yml new file mode 100644 index 000000000..bea376ba9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-element.yml @@ -0,0 +1,35 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Remove '/business/rating' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + state: absent + register: remove_element + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-remove-element.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_element is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml new file mode 100644 index 000000000..61b7179ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute-nochange.yml @@ -0,0 +1,37 @@ +--- +# 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 test fixture + copy: + src: results/test-remove-namespaced-attribute.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Remove non-existing namespaced '/bus:business/rat:rating/@attr:subjective' + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + xpath: /bus:business/rat:rating/@attr:subjective + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + state: absent + register: remove_namespaced_attribute + + - name: Compare to expected result + copy: + src: results/test-remove-namespaced-attribute.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_namespaced_attribute is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-namespaced-attribute.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.yml new file mode 100644 index 000000000..a725ee79c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-attribute.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: Setup test fixture + copy: + src: fixtures/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Remove namespaced '/bus:business/rat:rating/@attr:subjective' + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + xpath: /bus:business/rat:rating/@attr:subjective + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + state: absent + register: remove_namespaced_attribute + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-namespaced-beers.xml + + - name: Compare to expected result + copy: + src: results/test-remove-namespaced-attribute.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_namespaced_attribute is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-namespaced-attribute.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml new file mode 100644 index 000000000..fd83c54c3 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element-nochange.yml @@ -0,0 +1,37 @@ +--- +# 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 test fixture + copy: + src: results/test-remove-element.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Remove non-existing namespaced '/bus:business/rat:rating' + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + xpath: /bus:business/rat:rating + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + state: absent + register: remove_namespaced_element + + - name: Compare to expected result + copy: + src: results/test-remove-element.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_namespaced_element is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element.yml new file mode 100644 index 000000000..c4129f33e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-remove-namespaced-element.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: Setup test fixture + copy: + src: fixtures/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Remove namespaced '/bus:business/rat:rating' + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + xpath: /bus:business/rat:rating + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + state: absent + register: remove_namespaced_element + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-namespaced-beers.xml + + - name: Compare to expected result + copy: + src: results/test-remove-element.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - remove_namespaced_element is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-remove-element.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml new file mode 100644 index 000000000..bf35bfdd9 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value-unicode.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Set '/business/rating/@subjective' to 'нет' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + attribute: subjective + value: нет + register: set_attribute_value_unicode + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-attribute-value-unicode.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_attribute_value_unicode is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-attribute-value-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value.yml new file mode 100644 index 000000000..2908e00aa --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-attribute-value.yml @@ -0,0 +1,36 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Set '/business/rating/@subjective' to 'false' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + attribute: subjective + value: 'false' + register: set_attribute_value + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-attribute-value.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_attribute_value is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-attribute-value.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-level.yml new file mode 100644 index 000000000..648f5b25a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-level.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Set child elements + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + set_children: &children + - beer: + alcohol: "0.5" + name: 90 Minute IPA + _: + - Water: + liter: "0.2" + quantity: 200g + - Starch: + quantity: 10g + - Hops: + quantity: 50g + - Yeast: + quantity: 20g + - beer: + alcohol: "0.3" + name: Harvest Pumpkin Ale + _: + - Water: + liter: "0.2" + quantity: 200g + - Hops: + quantity: 25g + - Yeast: + quantity: 20g + register: set_children_elements_level + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-children-elements-level.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_elements_level is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-children-elements-level.xml /tmp/ansible-xml-beers.xml + + + - name: Set child elements (again) + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + set_children: *children + register: set_children_again + + - name: Compare to expected result + copy: + src: results/test-set-children-elements-level.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_again is not changed + - comparison is not changed # identical diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.yml new file mode 100644 index 000000000..8c4fc1094 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements-unicode.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Set child elements + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + set_children: &children + - beer: Окское + - beer: Невское + register: set_children_elements_unicode + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-children-elements-unicode.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_elements_unicode is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml + + + - name: Compare to expected result + copy: + src: results/test-set-children-elements-unicode.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_again is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-children-elements-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements.yml new file mode 100644 index 000000000..ed9e4a54e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-children-elements.yml @@ -0,0 +1,86 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + - name: Set child elements - empty list + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + set_children: [] + register: set_children_elements + + - name: Compare to expected result + copy: + src: results/test-set-children-elements-empty-list.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_elements is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + - name: Set child elements + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + set_children: &children + - beer: 90 Minute IPA + - beer: Harvest Pumpkin Ale + register: set_children_elements + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-children-elements.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_elements is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml + + + - name: Set child elements (again) + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/beers + set_children: *children + register: set_children_again + + - name: Compare to expected result + copy: + src: results/test-set-children-elements.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_children_again is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-children-elements.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml new file mode 100644 index 000000000..4041bf910 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-empty.yml @@ -0,0 +1,35 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Set '/business/website/address' to empty string. + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/website/address + value: '' + register: set_element_value_empty + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-element-value-empty.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_element_value_empty is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-element-value-empty.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml new file mode 100644 index 000000000..616f26ddc --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value-unicode.yml @@ -0,0 +1,50 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add 2nd '/business/rating' with value 'пять' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business + add_children: + - rating: пять + + - name: Set '/business/rating' to 'пять' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + value: пять + register: set_element_first_run + + - name: Set '/business/rating' to 'false'... again + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + value: пять + register: set_element_second_run + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-element-value-unicode.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_element_first_run is changed + - set_element_second_run is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-element-value-unicode.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value.yml new file mode 100644 index 000000000..b563b2576 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-element-value.yml @@ -0,0 +1,50 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-beers.xml + dest: /tmp/ansible-xml-beers.xml + + + - name: Add 2nd '/business/rating' with value '5' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business + add_children: + - rating: '5' + + - name: Set '/business/rating' to '5' + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + value: '5' + register: set_element_first_run + + - name: Set '/business/rating' to '5'... again + xml: + path: /tmp/ansible-xml-beers.xml + xpath: /business/rating + value: '5' + register: set_element_second_run + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-element-value.xml + dest: /tmp/ansible-xml-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_element_first_run is changed + - set_element_second_run is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-element-value.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml new file mode 100644 index 000000000..7c1bbd237 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-attribute-value.yml @@ -0,0 +1,41 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Set namespaced '/bus:business/rat:rating/@attr:subjective' to 'false' + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + xpath: /bus:business/rat:rating + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + attribute: attr:subjective + value: 'false' + register: set_namespaced_attribute_value + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-namespaced-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-namespaced-attribute-value.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - set_namespaced_attribute_value is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-set-namespaced-attribute-value.xml /tmp/ansible-xml-namespaced-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml new file mode 100644 index 000000000..e6ed1bdec --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-children-elements.yml @@ -0,0 +1,61 @@ +--- +# 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 test fixture + copy: + src: fixtures/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers-xml.xml + + - name: Set child elements + xml: + path: /tmp/ansible-xml-namespaced-beers-xml.xml + xpath: /bus:business/ber:beers + namespaces: + bus: http://test.business + ber: http://test.beers + set_children: + - beer: 90 Minute IPA + - beer: Harvest Pumpkin Ale + + - name: Copy state after first set_children + copy: + src: /tmp/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers-1.xml + remote_src: true + + - name: Set child elements again + xml: + path: /tmp/ansible-xml-namespaced-beers-xml.xml + xpath: /bus:business/ber:beers + namespaces: + bus: http://test.business + ber: http://test.beers + set_children: + - beer: 90 Minute IPA + - beer: Harvest Pumpkin Ale + register: set_children_again + + - name: Copy state after second set_children + copy: + src: /tmp/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers-2.xml + remote_src: true + + - name: Compare to expected result + copy: + src: /tmp/ansible-xml-namespaced-beers-1.xml + dest: /tmp/ansible-xml-namespaced-beers-2.xml + remote_src: true + check_mode: true + diff: true + register: comparison + #command: diff /tmp/ansible-xml-namespaced-beers-1.xml /tmp/ansible-xml-namespaced-beers-2.xml + + - name: Test expected result + assert: + that: + - set_children_again is not changed # idempotency + - set_namespaced_attribute_value is changed + - comparison is not changed # identical diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.yml new file mode 100644 index 000000000..9944da8a5 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-set-namespaced-element-value.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 + + - name: Setup test fixture + copy: + src: fixtures/ansible-xml-namespaced-beers.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + + + - name: Set namespaced '/bus:business/rat:rating' to '11' + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + xpath: /bus:business/rat:rating + value: '11' + register: set_element_first_run + + - name: Set namespaced '/bus:business/rat:rating' to '11' again + xml: + path: /tmp/ansible-xml-namespaced-beers.xml + namespaces: + bus: http://test.business + ber: http://test.beers + rat: http://test.rating + attr: http://test.attribute + xpath: /bus:business/rat:rating + value: '11' + register: set_element_second_run + + - name: Add trailing newline + shell: echo "" >> /tmp/ansible-xml-namespaced-beers.xml + + - name: Compare to expected result + copy: + src: results/test-set-namespaced-element-value.xml + dest: /tmp/ansible-xml-namespaced-beers.xml + check_mode: true + diff: true + register: comparison + #command: diff -u {{ role_path }}/results/test-set-namespaced-element-value.xml /tmp/ansible-xml-namespaced-beers.xml + + - name: Test expected result + assert: + that: + - set_element_first_run is changed + - set_element_second_run is not changed + - comparison is not changed # identical diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-xmlstring.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-xmlstring.yml new file mode 100644 index 000000000..1c2e4de4a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/test-xmlstring.yml @@ -0,0 +1,85 @@ +--- +# 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: Copy expected results to remote + copy: + src: "results/{{ item }}" + dest: "/tmp/{{ item }}" + with_items: + - test-pretty-print.xml + - test-pretty-print-only.xml + + # NOTE: Jinja2 templating eats trailing newlines + - name: Read from xmlstring (not using pretty_print) + xml: + xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}" + xpath: . + register: xmlresponse + + - name: Compare to expected result + copy: + content: "{{ xmlresponse.xmlstring }}\n" + dest: '/tmp/test-pretty-print-only.xml' + check_mode: true + diff: true + register: comparison + + - name: Test expected result + assert: + that: + - xmlresponse is not changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml + + + # NOTE: Jinja2 templating eats trailing newlines + - name: Read from xmlstring (using pretty_print) + xml: + xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}" + pretty_print: true + register: xmlresponse + + - name: Compare to expected result + copy: + content: '{{ xmlresponse.xmlstring }}' + dest: '/tmp/test-pretty-print-only.xml' + check_mode: true + diff: true + register: comparison + + # FIXME: This change is related to the newline added by pretty_print + - name: Test expected result + assert: + that: + - xmlresponse is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-pretty-print-only.xml /tmp/ansible-xml-beers.xml + + + # NOTE: Jinja2 templating eats trailing newlines + - name: Read from xmlstring + xml: + xmlstring: "{{ lookup('file', '{{ role_path }}/fixtures/ansible-xml-beers.xml') }}" + xpath: /business/beers + pretty_print: true + add_children: + - beer: Old Rasputin + register: xmlresponse_modification + + - name: Compare to expected result + copy: + content: '{{ xmlresponse_modification.xmlstring }}' + dest: '/tmp/test-pretty-print.xml' + check_mode: true + diff: true + register: comparison + + # FIXME: This change is related to the newline added by pretty_print + - name: Test expected result + assert: + that: + - xmlresponse_modification is changed + - comparison is not changed # identical + #command: diff -u {{ role_path }}/results/test-pretty-print.xml /tmp/ansible-xml-beers.xml diff --git a/ansible_collections/community/general/tests/integration/targets/xml/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/xml/vars/main.yml new file mode 100644 index 000000000..a8dfc2396 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/xml/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 + +# -*- mode: yaml -* + +bad_beers: +- beer: "Natty Lite" +- beer: "Miller Lite" +- beer: "Coors Lite" diff --git a/ansible_collections/community/general/tests/integration/targets/yarn/aliases b/ansible_collections/community/general/tests/integration/targets/yarn/aliases new file mode 100644 index 000000000..cb1bc115a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yarn/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/1 +destructive +skip/aix +skip/freebsd diff --git a/ansible_collections/community/general/tests/integration/targets/yarn/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/yarn/meta/main.yml new file mode 100644 index 000000000..6147ad33e --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yarn/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/yarn/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/yarn/tasks/main.yml new file mode 100644 index 000000000..e12d891c2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yarn/tasks/main.yml @@ -0,0 +1,23 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Yarn package manager integration tests +# Copyright (c) 2018 David Gunter, +# 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: run.yml + vars: + nodejs_version: '{{ item.node_version }}' + nodejs_path: 'node-v{{ nodejs_version }}-{{ ansible_system|lower }}-x{{ ansible_userspace_bits }}' + yarn_version: '{{ item.yarn_version }}' + with_items: + - {node_version: 4.8.0, yarn_version: 1.6.0} # Lowest compatible nodejs version + - {node_version: 8.0.0, yarn_version: 1.6.0} + when: + - not (ansible_os_family == 'Alpine') # TODO diff --git a/ansible_collections/community/general/tests/integration/targets/yarn/tasks/run.yml b/ansible_collections/community/general/tests/integration/targets/yarn/tasks/run.yml new file mode 100644 index 000000000..0d7d6fb42 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yarn/tasks/run.yml @@ -0,0 +1,233 @@ +--- +# 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 directory for Node' + file: + path: /usr/local/lib/nodejs + state: directory + +- name: 'Download Nodejs' + unarchive: + src: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yarn/{{ nodejs_path }}.tar.gz' + dest: '{{ remote_tmp_dir }}' + remote_src: true + creates: '{{ remote_tmp_dir }}/{{ nodejs_path }}.tar.gz' + +- name: 'Download Yarn' + unarchive: + src: 'https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/yarn/yarn-v{{yarn_version}}.tar.gz' + dest: '{{ remote_tmp_dir }}' + remote_src: true + creates: '{{ remote_tmp_dir }}/yarn-v{{yarn_version}}_pkg.tar.gz' + +- name: 'Copy node to directory created earlier' + command: "mv {{ remote_tmp_dir }}/{{ nodejs_path }} /usr/local/lib/nodejs/{{nodejs_path}}" + +# Clean up before running tests +- name: Remove any previous Nodejs modules + file: + path: '{{remote_tmp_dir}}/node_modules' + state: absent + +# Set vars for our test harness +- vars: + #node_bin_path: "/usr/local/lib/nodejs/node-v{{nodejs_version}}/bin" + node_bin_path: "/usr/local/lib/nodejs/{{ nodejs_path }}/bin" + yarn_bin_path: "{{ remote_tmp_dir }}/yarn-v{{ yarn_version }}/bin" + package: 'iconv-lite' + environment: + PATH: "{{ node_bin_path }}:{{ansible_env.PATH}}" + YARN_IGNORE_ENGINES: true + block: + + # Get the version of Yarn and register to a variable + - shell: '{{ yarn_bin_path }}/yarn --version' + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_version + + - name: 'Create dummy package.json' + template: + src: package.j2 + dest: '{{ remote_tmp_dir }}/package.json' + + - name: 'Install all packages.' + yarn: + path: '{{ remote_tmp_dir }}' + executable: '{{ yarn_bin_path }}/yarn' + state: present + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + + - name: 'Install the same package from package.json again.' + yarn: + path: '{{ remote_tmp_dir }}' + executable: '{{ yarn_bin_path }}/yarn' + name: '{{ package }}' + state: present + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_install + + - assert: + that: + - not (yarn_install is changed) + + - name: 'Install all packages in check mode.' + yarn: + path: '{{ remote_tmp_dir }}' + executable: '{{ yarn_bin_path }}/yarn' + state: present + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + check_mode: true + register: yarn_install_check + + - name: verify test yarn global installation in check mode + assert: + that: + - yarn_install_check.err is defined + - yarn_install_check.out is defined + - yarn_install_check.err is none + - yarn_install_check.out is none + + - name: 'Install package with explicit version (older version of package)' + yarn: + path: '{{ remote_tmp_dir }}' + executable: '{{ yarn_bin_path }}/yarn' + name: left-pad + version: 1.1.0 + state: present + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_install_old_package + + - assert: + that: + - yarn_install_old_package is changed + + - name: 'Again but without explicit executable path' + yarn: + path: '{{ remote_tmp_dir }}' + name: left-pad + version: 1.1.0 + state: present + environment: + PATH: '{{ yarn_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}' + + - name: 'Upgrade old package' + yarn: + path: '{{ remote_tmp_dir }}' + executable: '{{ yarn_bin_path }}/yarn' + name: left-pad + state: latest + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_update_old_package + + - assert: + that: + - yarn_update_old_package is changed + + - name: 'Remove a package' + yarn: + path: '{{ remote_tmp_dir }}' + executable: '{{ yarn_bin_path }}/yarn' + name: '{{ package }}' + state: absent + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_uninstall_package + + - name: 'Assert package removed' + assert: + that: + - yarn_uninstall_package is changed + + - name: 'Global install binary with explicit version (older version of package)' + yarn: + global: true + executable: '{{ yarn_bin_path }}/yarn' + name: prettier + version: 2.0.0 + state: present + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_global_install_old_binary + + - assert: + that: + - yarn_global_install_old_binary is changed + + - name: 'Global upgrade old binary' + yarn: + global: true + executable: '{{ yarn_bin_path }}/yarn' + name: prettier + state: latest + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_global_update_old_binary + + - assert: + that: + - yarn_global_update_old_binary is changed + + - name: 'Global remove a binary' + yarn: + global: true + executable: '{{ yarn_bin_path }}/yarn' + name: prettier + state: absent + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_global_uninstall_binary + + - assert: + that: + - yarn_global_uninstall_binary is changed + + - name: 'Global install package with no binary with explicit version (older version of package)' + yarn: + global: true + executable: '{{ yarn_bin_path }}/yarn' + name: left-pad + version: 1.1.0 + state: present + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_global_install_old_package + + - assert: + that: + - yarn_global_install_old_package is changed + + - name: 'Global upgrade old package with no binary' + yarn: + global: true + executable: '{{ yarn_bin_path }}/yarn' + name: left-pad + state: latest + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_global_update_old_package + + - assert: + that: + - yarn_global_update_old_package is changed + + - name: 'Global remove a package with no binary' + yarn: + global: true + executable: '{{ yarn_bin_path }}/yarn' + name: left-pad + state: absent + environment: + PATH: '{{ node_bin_path }}:{{ ansible_env.PATH }}' + register: yarn_global_uninstall_package + + - assert: + that: + - yarn_global_uninstall_package is changed diff --git a/ansible_collections/community/general/tests/integration/targets/yarn/templates/package.j2 b/ansible_collections/community/general/tests/integration/targets/yarn/templates/package.j2 new file mode 100644 index 000000000..3f5456ad2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yarn/templates/package.j2 @@ -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": "ansible-yarn-testing", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.4.21", + "@types/node": "^12.0.0" + } +} diff --git a/ansible_collections/community/general/tests/integration/targets/yum_versionlock/aliases b/ansible_collections/community/general/tests/integration/targets/yum_versionlock/aliases new file mode 100644 index 000000000..ca3f7e796 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yum_versionlock/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/1 +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel8.4 # TODO make sure that tests work on 8.4 as well! +disabled # TODO diff --git a/ansible_collections/community/general/tests/integration/targets/yum_versionlock/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/yum_versionlock/tasks/main.yml new file mode 100644 index 000000000..05f1f7495 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/yum_versionlock/tasks/main.yml @@ -0,0 +1,87 @@ +--- +#################################################################### +# 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: Update procps-ng temporary until issue (#2539) is fixed + yum: + name: procps-ng + state: latest + when: ansible_distribution == 'Fedora' and ansible_distribution_major_version == '34' + +- block: + - name: Install necessary packages to test yum_versionlock + yum: + name: yum-plugin-versionlock + state: present + register: yum_versionlock_install + + - name: Yum checkupdate + yum: + list: updates + register: yum_updates + + - block: + - name: Lock all packages + community.general.yum_versionlock: + name: "{{ yum_updates.results | map(attribute='name') | list }}" + state: present + register: lock_all_packages + + - name: Lock all packages again + community.general.yum_versionlock: + name: "{{ yum_updates.results | map(attribute='name') | list }}" + state: present + register: lock_all_packages_again + + - name: Lock packages wildcard + community.general.yum_versionlock: + name: "nss*" + state: present + register: lock_nss_wildcard + + # This should fail when it needs user interaction and missing -y is on purpose. + - name: Update all packages (not really) + command: yum update --setopt=obsoletes=0 + register: update_all_locked_packages + changed_when: + - '"No packages marked for update" not in update_all_locked_packages.stdout' + - '"Nothing to do" not in update_all_locked_packages.stdout' + + - name: Unlock all packages + community.general.yum_versionlock: + name: "{{ yum_updates.results | map(attribute='name') | list }}" + state: absent + register: unlock_all_packages + + - name: Update all packages + yum: + name: '*' + state: latest + check_mode: true + register: update_all_packages + when: yum_updates.results | length != 0 + + - name: Assert everything is fine + assert: + that: + - lock_all_packages is changed + - lock_all_packages_again is not changed + - lock_nss_wildcard is not changed + - update_all_locked_packages is not changed + - unlock_all_packages is changed + - update_all_packages is changed + when: yum_updates.results | length != 0 + + - name: Remove installed packages in case it was not installed + yum: + name: yum-plugin-versionlock + state: absent + when: yum_versionlock_install is changed + when: (ansible_distribution in ['CentOS', 'RedHat'] and ansible_distribution_major_version is version('7', '>=')) or + (ansible_distribution == 'Fedora') diff --git a/ansible_collections/community/general/tests/integration/targets/zypper/aliases b/ansible_collections/community/general/tests/integration/targets/zypper/aliases new file mode 100644 index 000000000..e0a62a673 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/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/1 +destructive +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec b/ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec new file mode 100644 index 000000000..044ea3a54 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec @@ -0,0 +1,12 @@ +Summary: Empty RPM +Name: empty +Version: 1 +Release: 0 +License: GPLv3 +Group: Applications/System +BuildArch: noarch + +%description +Empty RPM + +%files diff --git a/ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec.license b/ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/files/empty.spec.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: Ansible Project diff --git a/ansible_collections/community/general/tests/integration/targets/zypper/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/zypper/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/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/zypper/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/zypper/tasks/main.yml new file mode 100644 index 000000000..185f2f90a --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/tasks/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 # +#################################################################### + +# test code for the zypper module +# Copyright 2015, Guido Günther +# heavily based on the yum tests which are +# Copyright 2014, James Tanner +# 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: 'zypper.yml' + when: ansible_os_family == 'Suse' diff --git a/ansible_collections/community/general/tests/integration/targets/zypper/tasks/zypper.yml b/ansible_collections/community/general/tests/integration/targets/zypper/tasks/zypper.yml new file mode 100644 index 000000000..3eefddbdf --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/tasks/zypper.yml @@ -0,0 +1,530 @@ +--- +# 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: get hello package version + shell: zypper --xmlout se -svx hello | grep 'name="hello"' | grep 'repository="Main Repository"' | sed 's/.*edition="\([^ ]*\)".*/\1/' + register: hello_version + +- name: set URL of test package + set_fact: + hello_package_url: https://download.opensuse.org/distribution/leap/{{ ansible_distribution_version }}/repo/oss/x86_64/hello-{{ hello_version.stdout }}.x86_64.rpm + +- debug: var=hello_package_url + +# UNINSTALL +- name: uninstall hello + zypper: + name: hello + state: removed + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: false + register: rpm_result + +- debug: var=zypper_result +- debug: var=rpm_result + +- name: verify uninstallation of hello + assert: + that: + - "zypper_result.rc == 0" + - "rpm_result.rc == 1" + +# UNINSTALL AGAIN +- name: uninstall hello again + zypper: + name: hello + state: removed + register: zypper_result + +- name: verify no change on re-uninstall + assert: + that: + - "not zypper_result.changed" + +# INSTALL +- name: install hello + zypper: + name: hello + state: present + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: false + register: rpm_result + +- debug: var=zypper_result +- debug: var=rpm_result + +- name: verify installation of hello + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_result.rc == 0" + +# INSTALL AGAIN +- name: install hello again + zypper: + name: hello + state: present + register: zypper_result + +- name: verify no change on second install + assert: + that: + - "not zypper_result.changed" + +# Multiple packages +- name: uninstall hello and metamail + zypper: + name: + - hello + - metamail + state: removed + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: false + register: rpm_hello_result + +- name: check metamail with rpm + shell: rpm -q metamail + failed_when: false + register: rpm_metamail_result + +- name: verify packages uninstalled + assert: + that: + - "rpm_hello_result.rc != 0" + - "rpm_metamail_result.rc != 0" + +- name: install hello and metamail + zypper: + name: + - hello + - metamail + state: present + register: zypper_result + +- name: check hello with rpm + shell: rpm -q hello + failed_when: false + register: rpm_hello_result + +- name: check metamail with rpm + shell: rpm -q metamail + failed_when: false + register: rpm_metamail_result + +- name: verify packages installed + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_hello_result.rc == 0" + - "rpm_metamail_result.rc == 0" + +- name: uninstall hello and metamail + zypper: + name: + - hello + - metamail + state: removed + +# INSTALL nonexistent package +- name: install hello from url + zypper: + name: doesnotexist + state: present + register: zypper_result + ignore_errors: true + +- name: verify package installation failed + assert: + that: + - "zypper_result.rc == 104" + - "zypper_result.msg.startswith('No provider of')" + +# INSTALL broken local package +- name: create directory + file: + path: "{{remote_tmp_dir | expanduser}}/zypper1" + state: directory + +- name: fake rpm package + file: + path: "{{remote_tmp_dir | expanduser}}/zypper1/broken.rpm" + state: touch + +- name: install broken rpm + zypper: + name: "{{remote_tmp_dir | expanduser}}/zypper1/broken.rpm" + state: present + register: zypper_result + ignore_errors: true + +- debug: var=zypper_result + +- name: verify we failed installation of broken rpm + assert: + that: + - "zypper_result.rc == 3" + - "'Problem reading the RPM header' in zypper_result.stdout" + +# Build and install an empty rpm +- name: uninstall empty + zypper: + name: empty + state: removed + +- name: install rpmbuild + zypper: + name: rpmbuild + state: present + +- name: clean zypper RPM cache + file: + name: /var/cache/zypper/RPMS + state: absent + +- name: create directory + file: + path: "{{remote_tmp_dir | expanduser}}/zypper2" + state: directory + +- name: copy spec file + copy: + src: empty.spec + dest: "{{ remote_tmp_dir | expanduser }}/zypper2/empty.spec" + +- name: build rpm + command: | + rpmbuild -bb \ + --define "_topdir {{remote_tmp_dir | expanduser }}/zypper2/rpm-build" + --define "_builddir %{_topdir}" \ + --define "_rpmdir %{_topdir}" \ + --define "_srcrpmdir %{_topdir}" \ + --define "_specdir {{remote_tmp_dir | expanduser}}/zypper2" \ + --define "_sourcedir %{_topdir}" \ + {{ remote_tmp_dir }}/zypper2/empty.spec + register: rpm_build_result + +- name: install empty rpm + zypper: + name: "{{ remote_tmp_dir | expanduser }}/zypper2/rpm-build/noarch/empty-1-0.noarch.rpm" + disable_gpg_check: true + register: zypper_result + +- name: check empty with rpm + shell: rpm -q empty + failed_when: false + register: rpm_result + +- name: verify installation of empty + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_result.rc == 0" + +- name: uninstall empty + zypper: + name: empty + state: removed + +- name: extract from rpm + zypper: + name: "{{ remote_tmp_dir | expanduser }}/zypper2/rpm-build/noarch/empty-1-0.noarch.rpm" + state: installed + disable_gpg_check: true + extra_args_precommand: --root {{ remote_tmp_dir | expanduser }}/testdir/ + +- name: check that dir var is exist + stat: path={{ remote_tmp_dir | expanduser }}/testdir/var + register: stat_result + +- name: check that we extract rpm package in testdir folder and folder var is exist + assert: + that: + - "stat_result.stat.exists == true" + + +# test simultaneous remove and install using +- prefixes + +- name: install hello to prep next task + zypper: + name: hello + state: present + +- name: remove metamail to prep next task + zypper: + name: metamail + state: absent + +- name: install and remove in the same run, with +- prefix + zypper: + name: + - -hello + - +metamail + state: present + register: zypper_res1 + +- name: install and remove again, leave out plus + zypper: + name: + - metamail + - -hello + state: present + register: zypper_res1a + +- name: in and rm swapped + zypper: + name: + - -metamail + - hello + state: present + register: zypper_res1b + +- name: install metamail + zypper: + name: metamail + state: absent + register: zypper_res2 + +- name: remove hello + zypper: + name: hello + state: present + register: zypper_res3 + +- name: verify simultaneous install/remove worked + assert: + that: + - zypper_res1 is successful + - zypper_res1 is changed + - zypper_res1a is not changed + - zypper_res1b is changed + - zypper_res2 is not changed + - zypper_res3 is not changed + + +- name: install and remove with state=absent + zypper: + name: + - metamail + - +hello + state: absent + register: zypper_res + ignore_errors: true + +- name: verify simultaneous install/remove failed with absent + assert: + that: + - zypper_res is failed + - zypper_res.msg == "Can not combine '+' prefix with state=remove/absent." + +- name: try rm patch + zypper: + name: openSUSE-2016-128 + type: patch + state: absent + ignore_errors: true + register: zypper_patch +- assert: + that: + - zypper_patch is failed + - zypper_patch.msg.startswith('Can not remove patches.') + +- name: try rm URL + zypper: + name: "{{ hello_package_url }}" + state: absent + ignore_errors: true + register: zypper_rm +- assert: + that: + - zypper_rm is failed + - zypper_rm.msg.startswith('Can not remove via URL.') + +- name: remove pattern update_test + zypper: + name: update_test + type: pattern + state: absent + +- name: install pattern update_test + zypper: + name: update_test + type: pattern + state: present + register: zypper_install_pattern1 + +- name: install pattern update_test again + zypper: + name: update_test + type: pattern + state: present + register: zypper_install_pattern2 + +- assert: + that: + - zypper_install_pattern1 is changed + - zypper_install_pattern2 is not changed + +- name: remove hello + zypper: + name: hello + state: absent + +- name: install via URL + zypper: + state: present + name: "{{ hello_package_url }}" + register: zypperin1 + +- name: test install + zypper: + name: hello + state: present + register: zypperin2 + +- assert: + that: + - zypperin1 is succeeded + - zypperin1 is changed + - zypperin2 is not changed + +# check for https://github.com/ansible/ansible/issues/20139 +- name: run updatecache + zypper: + name: hello + state: present + update_cache: true + register: zypper_result_update_cache + +- name: run updatecache in check mode + zypper: + name: hello + state: present + update_cache: true + check_mode: true + register: zypper_result_update_cache_check + + +- assert: + that: + - zypper_result_update_cache is successful + - zypper_result_update_cache_check is successful + - zypper_result_update_cache_check is not changed + +# - name: ensure no previous netcat package still exists +# zypper: +# name: +# - netcat-openbsd +# - gnu-netcat +# state: absent +# +# - name: install netcat-openbsd which conflicts with gnu-netcat +# zypper: +# name: netcat-openbsd +# state: present +# +# - name: try installation of gnu-netcat which should fail due to the conflict +# zypper: +# name: gnu-netcat +# state: present +# ignore_errors: true +# register: zypper_pkg_conflict +# +# - assert: +# that: +# - zypper_pkg_conflict is failed +# - "'conflicts with netcat-openbsd provided' in zypper_pkg_conflict.stdout" +# +# - name: retry installation of gnu-netcat with force_resolution set to choose a resolution +# zypper: +# name: gnu-netcat +# state: present +# force_resolution: True + +- name: duplicate rpms block + vars: + looplist: + - 1 + - 2 + block: + - name: Deploy spec files to build 2 packages with duplicate files. + template: + src: duplicate.spec.j2 + dest: "{{ remote_tmp_dir | expanduser }}/zypper2/duplicate{{ item }}.spec" + loop: "{{ looplist }}" + + - name: build rpms with duplicate files + command: | + rpmbuild -bb \ + --define "_topdir {{remote_tmp_dir | expanduser }}/zypper2/rpm-build" + --define "_builddir %{_topdir}" \ + --define "_rpmdir %{_topdir}" \ + --define "_srcrpmdir %{_topdir}" \ + --define "_specdir {{remote_tmp_dir | expanduser}}/zypper2" \ + --define "_sourcedir %{_topdir}" \ + {{ remote_tmp_dir | expanduser }}/zypper2/duplicate{{ item }}.spec + loop: "{{ looplist }}" + + - name: install duplicate rpms + zypper: + name: >- + {{ remote_tmp_dir | expanduser }}/zypper2/rpm-build/noarch/duplicate{{ item }}-1-0.noarch.rpm + disable_gpg_check: true + ignore_errors: true + register: zypper_duplicate_result + loop: "{{ looplist }}" + + - name: Read in duplicate file contents + slurp: + src: /usr/lib/duplicate/duplicate.txt + register: duplicate_out + + - name: Check failure when installing rpms with duplicate files without replacefiles option + assert: + that: + - zypper_duplicate_result.results[0] is successful + - zypper_duplicate_result.results[1] is failed + - '"fileconflict" in zypper_duplicate_result.results[1].stdout' + - '"/usr/lib/duplicate/duplicate.txt" in zypper_duplicate_result.results[1].stdout' + - '"duplicate1" in duplicate_out.content | b64decode' + + - name: install duplicate rpms + zypper: + name: >- + {{ remote_tmp_dir | expanduser }}/zypper2/rpm-build/noarch/duplicate{{ item }}-1-0.noarch.rpm + disable_gpg_check: true + replacefiles: true + ignore_errors: true + register: zypper_duplicate_result + loop: "{{ looplist }}" + + - name: Read in duplicate file contents + slurp: + src: /usr/lib/duplicate/duplicate.txt + register: duplicate_out + + - name: Check success installing rpms with duplicate files using replacefiles option + assert: + that: + - zypper_duplicate_result is successful + - zypper_duplicate_result is changed + - '"duplicate2" in duplicate_out.content | b64decode' + + - name: Remove installed duplicate rpms + zypper: + name: "duplicate{{ item }}-1-0" + state: absent + loop: "{{ looplist }}" diff --git a/ansible_collections/community/general/tests/integration/targets/zypper/templates/duplicate.spec.j2 b/ansible_collections/community/general/tests/integration/targets/zypper/templates/duplicate.spec.j2 new file mode 100644 index 000000000..6f63b665c --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper/templates/duplicate.spec.j2 @@ -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 +#} + +Summary: Duplicate{{ item }} RPM. Installs one file that is a duplicate of other Duplicate# RPMs +Name: duplicate{{ item }} +Version: 1 +Release: 0 +License: GPLv3 +Group: Applications/System +BuildArch: noarch + +%description +Duplicate {{ item }} RPM. Package one file that will be a duplicate of other Duplicate RPM contents. +This is only for testing of the replacefiles zypper option. + +%install +mkdir -p "%{buildroot}/usr/lib/duplicate" +echo "%{name}" > "%{buildroot}/usr/lib/duplicate/duplicate.txt" + +%files +/usr/lib/duplicate/duplicate.txt diff --git a/ansible_collections/community/general/tests/integration/targets/zypper_repository/aliases b/ansible_collections/community/general/tests/integration/targets/zypper_repository/aliases new file mode 100644 index 000000000..e0a62a673 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper_repository/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/1 +destructive +skip/aix +skip/freebsd +skip/osx +skip/macos +skip/rhel diff --git a/ansible_collections/community/general/tests/integration/targets/zypper_repository/files/systemsmanagement_Uyuni_Utils.repo b/ansible_collections/community/general/tests/integration/targets/zypper_repository/files/systemsmanagement_Uyuni_Utils.repo new file mode 100644 index 000000000..aaa486d92 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper_repository/files/systemsmanagement_Uyuni_Utils.repo @@ -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 + +[systemsmanagement_Uyuni_Utils] +name=Several utilities to develop, build or release Uyuni (openSUSE_Leap_15.3) +type=rpm-md +baseurl=https://download.opensuse.org/repositories/systemsmanagement:/Uyuni:/Utils/openSUSE_Leap_15.3/ +gpgcheck=1 +gpgkey=https://download.opensuse.org/repositories/systemsmanagement:/Uyuni:/Utils/openSUSE_Leap_15.3/repodata/repomd.xml.key +enabled=1 diff --git a/ansible_collections/community/general/tests/integration/targets/zypper_repository/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/zypper_repository/meta/main.yml new file mode 100644 index 000000000..982de6eb0 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper_repository/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/zypper_repository/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/main.yml new file mode 100644 index 000000000..1d655a56f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/main.yml @@ -0,0 +1,13 @@ +--- +#################################################################### +# 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 zypper repository module +# Copyright (c) 2016, Guido Günther +# 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: 'test.yml' + when: ansible_os_family == 'Suse' diff --git a/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/test.yml new file mode 100644 index 000000000..739b4c264 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/test.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: collect repo configuration before test + shell: "grep . /etc/zypp/repos.d/*" + register: before + +- name: ensure zypper ref works + command: zypper -n ref + +- block: + - include_tasks: 'zypper_repository.yml' + always: + - name: remove repositories added during test + community.general.zypper_repository: + name: "{{item}}" + state: absent + with_items: + - chrome1 + - chrome2 + - test + - testrefresh + - testprio + - Apache_PHP_Modules + - systemsmanagement_Uyuni_Stable + - systemsmanagement_Uyuni_Utils + + - name: collect repo configuration after test + shell: "grep . /etc/zypp/repos.d/*" + register: after + + - name: verify repo configuration has been restored + assert: + that: + - before.stdout == after.stdout + + - name: ensure zypper ref still works + command: zypper -n ref diff --git a/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml b/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml new file mode 100644 index 000000000..ec362af10 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/zypper_repository/tasks/zypper_repository.yml @@ -0,0 +1,289 @@ +--- +# 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: Delete test repo + community.general.zypper_repository: + name: test + state: absent + register: zypper_result + +- name: verify no change on test repo deletion + assert: + that: + - "not zypper_result.changed" + +- name: Add test repo + community.general.zypper_repository: + name: test + state: present + repo: http://dl.google.com/linux/chrome/rpm/stable/x86_64 + register: zypper_result + +- name: verify repo addition + assert: + that: + - "zypper_result.changed" + +- name: Add same repo again + community.general.zypper_repository: + name: test + state: present + repo: http://dl.google.com/linux/chrome/rpm/stable/x86_64 + register: zypper_result + +- name: verify no change on second install + assert: + that: + - "not zypper_result.changed" + +- name: Change repo URL + community.general.zypper_repository: + name: test + state: present + repo: http://download.videolan.org/pub/vlc/SuSE/Leap_{{ ansible_distribution_version }}/ + register: zypper_result + +- name: Verify change on URL only change + assert: + that: + - "zypper_result.changed" + +- name: use refresh option + community.general.zypper_repository: + name: testrefresh + refresh: false + state: present + repo: http://download.videolan.org/pub/vlc/SuSE/Leap_{{ ansible_distribution_version }}/ + +- name: check refreshoption + command: zypper -x lr testrefresh + register: zypper_result + +- name: verify autorefresh option set properly + assert: + that: + - '"autorefresh=\"0\"" in zypper_result.stdout' + +- name: set repo priority + community.general.zypper_repository: + name: testprio + priority: 55 + state: present + repo: http://download.videolan.org/pub/vlc/SuSE/Leap_{{ ansible_distribution_version }}/ + +- name: check refreshoption + command: zypper -x lr testprio + register: zypper_result + +- name: verify priority option set properly + assert: + that: + - '"priority=\"55\"" in zypper_result.stdout' + +- name: add two repos with same url + community.general.zypper_repository: + name: "{{item}}" + state: present + repo: http://dl.google.com/linux/chrome/rpm/stable/x86_64 + with_items: + - chrome1 + - chrome2 + +- name: check repo is updated by url + command: zypper lr chrome1 + register: zypper_result1 + ignore_errors: true + +- name: check repo is updated by url + command: zypper lr chrome2 + register: zypper_result2 + +- name: ensure same url cause update of existing repo even if name differ + assert: + that: + - "zypper_result1.rc != 0" + - "'not found' in zypper_result1.stderr" + - "zypper_result2.rc == 0" + - "'http://dl.google.com/linux/chrome/rpm/stable/x86_64' in zypper_result2.stdout" + +- name: add two repos with same name + community.general.zypper_repository: + name: samename + state: present + repo: "{{ item }}" + with_items: + - http://download.opensuse.org/repositories/science/openSUSE_Leap_{{ ansible_distribution_version }}/ + - http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_{{ ansible_distribution_version }}/ + +- name: check repo is updated by name + command: zypper lr samename + register: zypper_result + +- name: ensure url get updated on repo with same name + assert: + that: + - "'/science/' not in zypper_result.stdout" + - "'/devel:/languages:/ruby/' in zypper_result.stdout" + +- name: remove last added repos (by URL to test that) + community.general.zypper_repository: + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_{{ ansible_distribution_version }}/ + state: absent + +# FIXME: this currently fails with `Repository 'Apache_PHP_Modules' is invalid.` +# - name: "Test adding a repo with custom GPG key" +# community.general.zypper_repository: +# name: "Apache_PHP_Modules" +# repo: "http://download.opensuse.org/repositories/server:/php:/applications/openSUSE_Tumbleweed/" +# priority: 100 +# auto_import_keys: true +# state: "present" + +- name: add a repo by releasever + community.general.zypper_repository: + name: releaseverrepo + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_$releasever/ + state: present + register: add_repo + +- name: add a repo by releasever again + community.general.zypper_repository: + name: releaseverrepo + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_$releasever/ + state: present + register: add_repo_again + +- name: no update in case of $releasever usage in url + assert: + that: + - add_repo is changed + - add_repo_again is not changed + +- name: remove added repo + community.general.zypper_repository: + repo: http://download.opensuse.org/repositories/devel:/languages:/ruby/openSUSE_Leap_{{ ansible_distribution_version }}/ + state: absent + register: remove_repo + +- name: verify repo was removed + assert: + that: + - remove_repo is changed + +- name: get list of files in /etc/zypp/repos.d/ + command: ls /etc/zypp/repos.d/ + changed_when: false + register: releaseverrepo_etc_zypp_reposd + +- name: verify removal of file releaseverrepo.repo in /etc/zypp/repos.d/ + assert: + that: + - "'releaseverrepo' not in releaseverrepo_etc_zypp_reposd.stdout" + +- name: add a repo by basearch + community.general.zypper_repository: + name: basearchrepo + repo: https://packagecloud.io/netdata/netdata/opensuse/13.2/$basearch + state: present + register: add_repo + +- name: add a repo by basearch again + community.general.zypper_repository: + name: basearchrepo + repo: https://packagecloud.io/netdata/netdata/opensuse/13.2/$basearch + state: present + register: add_repo_again + +- name: no update in case of $basearch usage in url + assert: + that: + - add_repo is changed + - add_repo_again is not changed + +- name: remove added repo + community.general.zypper_repository: + repo: https://packagecloud.io/netdata/netdata/opensuse/13.2/x86_64 + state: absent + register: remove_repo + +- name: verify repo was removed + assert: + that: + - remove_repo is changed + +# For now, the URL does not work for 15.4 +# FIXME: Try to get this working with newer versions +# (Maybe 'Uyuni' needs to be replaced with something else?) +- when: ansible_distribution_version is version('15.4', '<') + block: + - name: add new repository via url to .repo file + community.general.zypper_repository: + repo: http://download.opensuse.org/repositories/systemsmanagement:/Uyuni:/Stable/openSUSE_Leap_{{ ansible_distribution_version }}/systemsmanagement:Uyuni:Stable.repo + state: present + register: added_by_repo_file + + - name: get repository details from zypper + command: zypper lr systemsmanagement_Uyuni_Stable + register: get_repository_details_from_zypper + + - name: verify adding via .repo file was successful + assert: + that: + - "added_by_repo_file is changed" + - "get_repository_details_from_zypper.rc == 0" + - "'/systemsmanagement:/Uyuni:/Stable/' in get_repository_details_from_zypper.stdout" + + - name: add same repository via url to .repo file again to verify idempotency + community.general.zypper_repository: + repo: http://download.opensuse.org/repositories/systemsmanagement:/Uyuni:/Stable/openSUSE_Leap_{{ ansible_distribution_version }}/systemsmanagement:Uyuni:Stable.repo + state: present + register: added_again_by_repo_file + + - name: verify nothing was changed adding a repo with the same .repo file + assert: + that: + - added_again_by_repo_file is not changed + + - name: remove repository via url to .repo file + community.general.zypper_repository: + repo: http://download.opensuse.org/repositories/systemsmanagement:/Uyuni:/Stable/openSUSE_Leap_{{ ansible_distribution_version }}/systemsmanagement:Uyuni:Stable.repo + state: absent + register: removed_by_repo_file + + - name: get list of files in /etc/zypp/repos.d/ + command: ls /etc/zypp/repos.d/ + changed_when: false + register: etc_zypp_reposd + + - name: verify removal via .repo file was successful, including cleanup of local .repo file in /etc/zypp/repos.d/ + assert: + that: + - "removed_by_repo_file" + - "'/systemsmanagement:/Uyuni:/Stable/' not in etc_zypp_reposd.stdout" + +# FIXME: THIS DOESN'T SEEM TO WORK ANYMORE WITH ANY OPENSUSE VERSION IN CI! +- when: false + block: + - name: Copy test .repo file + copy: + src: 'files/systemsmanagement_Uyuni_Utils.repo' + dest: '{{ remote_tmp_dir }}' + + - name: add new repository via local path to .repo file + community.general.zypper_repository: + repo: "{{ remote_tmp_dir }}/systemsmanagement_Uyuni_Utils.repo" + state: present + register: added_by_repo_local_file + + - name: get repository details for systemsmanagement_Uyuni_Utils from zypper + command: zypper lr systemsmanagement_Uyuni_Utils + register: get_repository_details_from_zypper_for_systemsmanagement_Uyuni_Utils + + - name: verify adding repository via local .repo file was successful + assert: + that: + - "added_by_repo_local_file is changed" + - "get_repository_details_from_zypper_for_systemsmanagement_Uyuni_Utils.rc == 0" + - "'/systemsmanagement:/Uyuni:/Utils/' in get_repository_details_from_zypper_for_systemsmanagement_Uyuni_Utils.stdout" diff --git a/ansible_collections/community/general/tests/sanity/extra/aliases.json b/ansible_collections/community/general/tests/sanity/extra/aliases.json new file mode 100644 index 000000000..dabdcd6a1 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/aliases.json @@ -0,0 +1,11 @@ +{ + "include_symlinks": false, + "prefixes": [ + ".azure-pipelines/azure-pipelines.yml", + "tests/integration/targets/" + ], + "output": "path-message", + "requirements": [ + "PyYAML" + ] +} diff --git a/ansible_collections/community/general/tests/sanity/extra/aliases.json.license b/ansible_collections/community/general/tests/sanity/extra/aliases.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/aliases.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/extra/aliases.py b/ansible_collections/community/general/tests/sanity/extra/aliases.py new file mode 100755 index 000000000..c1dcba0df --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/aliases.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# 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 +"""Check extra collection docs with antsibull-docs.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys + +import yaml + + +def main(): + """Main entry point.""" + paths = sys.argv[1:] or sys.stdin.read().splitlines() + paths = [path for path in paths if path.endswith('/aliases')] + + with open('.azure-pipelines/azure-pipelines.yml', 'rb') as f: + azp = yaml.safe_load(f) + + allowed_targets = set(['azp/generic/1']) + for stage in azp['stages']: + if stage['stage'].startswith(('Sanity', 'Unit', 'Generic', 'Summary')): + continue + for job in stage['jobs']: + for group in job['parameters']['groups']: + allowed_targets.add('azp/posix/{0}'.format(group)) + + for path in paths: + targets = [] + skip = False + with open(path, 'r') as f: + for line in f: + if '#' in line: + line = line[:line.find('#')] + line = line.strip() + if line.startswith('needs/'): + continue + if line.startswith('skip/'): + continue + if line.startswith('cloud/'): + continue + if line.startswith('context/'): + continue + if line in ('unsupported', 'disabled', 'hidden'): + skip = True + if line in ('destructive', ): + continue + if '/' not in line: + continue + targets.append(line) + if skip: + continue + if not targets: + if 'targets/setup_' in path: + continue + print('%s: %s' % (path, 'found no targets')) + for target in targets: + if target not in allowed_targets: + print('%s: %s' % (path, 'found invalid target "{0}"'.format(target))) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/sanity/extra/botmeta.json b/ansible_collections/community/general/tests/sanity/extra/botmeta.json new file mode 100644 index 000000000..c546ab5fd --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/botmeta.json @@ -0,0 +1,8 @@ +{ + "include_symlinks": false, + "output": "path-line-column-message", + "requirements": [ + "PyYAML", + "voluptuous==0.12.1" + ] +} diff --git a/ansible_collections/community/general/tests/sanity/extra/botmeta.json.license b/ansible_collections/community/general/tests/sanity/extra/botmeta.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/botmeta.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/extra/botmeta.py b/ansible_collections/community/general/tests/sanity/extra/botmeta.py new file mode 100755 index 000000000..3b6c34834 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/botmeta.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python +# 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 +"""Check BOTMETA file.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import ast +import os +import re +import sys + +import yaml + +from voluptuous import Any, MultipleInvalid, PREVENT_EXTRA, Schema +from voluptuous.humanize import humanize_error + + +IGNORE_NO_MAINTAINERS = [ + 'plugins/cache/memcached.py', + 'plugins/cache/redis.py', + 'plugins/callback/cgroup_memory_recap.py', + 'plugins/callback/context_demo.py', + 'plugins/callback/counter_enabled.py', + 'plugins/callback/hipchat.py', + 'plugins/callback/jabber.py', + 'plugins/callback/log_plays.py', + 'plugins/callback/logdna.py', + 'plugins/callback/logentries.py', + 'plugins/callback/null.py', + 'plugins/callback/selective.py', + 'plugins/callback/slack.py', + 'plugins/callback/splunk.py', + 'plugins/callback/yaml.py', + 'plugins/inventory/nmap.py', + 'plugins/inventory/virtualbox.py', + 'plugins/connection/chroot.py', + 'plugins/connection/iocage.py', + 'plugins/connection/lxc.py', + 'plugins/lookup/cartesian.py', + 'plugins/lookup/chef_databag.py', + 'plugins/lookup/consul_kv.py', + 'plugins/lookup/credstash.py', + 'plugins/lookup/cyberarkpassword.py', + 'plugins/lookup/flattened.py', + 'plugins/lookup/keyring.py', + 'plugins/lookup/lastpass.py', + 'plugins/lookup/passwordstore.py', + 'plugins/lookup/shelvefile.py', + 'plugins/filter/json_query.py', + 'plugins/filter/random_mac.py', +] + +FILENAME = '.github/BOTMETA.yml' + +LIST_ENTRIES = frozenset(('supershipit', 'maintainers', 'labels', 'keywords', 'notify', 'ignore')) + +AUTHOR_REGEX = re.compile(r'^\w.*\(@([\w-]+)\)(?![\w.])') + + +def read_authors(filename): + data = {} + try: + with open(filename, 'rb') as b_module_data: + M = ast.parse(b_module_data.read()) + + for child in M.body: + if isinstance(child, ast.Assign): + for t in child.targets: + try: + theid = t.id + except AttributeError: + # skip errors can happen when trying to use the normal code + continue + + if theid == 'DOCUMENTATION': + if isinstance(child.value, ast.Dict): + data = ast.literal_eval(child.value) + else: + data = yaml.safe_load(child.value.s) + + except Exception as e: + print('%s:%d:%d: Cannot load DOCUMENTATION: %s' % (filename, 0, 0, e)) + return [] + + author = data.get('author') or [] + if isinstance(author, str): + author = [author] + return author + + +def extract_author_name(author): + m = AUTHOR_REGEX.match(author) + if m: + return m.group(1) + if author == 'Ansible Core Team': + return '$team_ansible_core' + return None + + +def validate(filename, filedata): + if not filename.startswith('plugins/'): + return + if filename.startswith(('plugins/doc_fragments/', 'plugins/module_utils/')): + return + # Compile lis tof all active and inactive maintainers + all_maintainers = filedata['maintainers'] + filedata['ignore'] + if not filename.startswith('plugins/filter/'): + maintainers = read_authors(filename) + for maintainer in maintainers: + maintainer = extract_author_name(maintainer) + if maintainer is not None and maintainer not in all_maintainers: + msg = 'Author %s not mentioned as active or inactive maintainer for %s (mentioned are: %s)' % ( + maintainer, filename, ', '.join(all_maintainers)) + print('%s:%d:%d: %s' % (FILENAME, 0, 0, msg)) + should_have_no_maintainer = filename in IGNORE_NO_MAINTAINERS + if not all_maintainers and not should_have_no_maintainer: + print('%s:%d:%d: %s' % (FILENAME, 0, 0, 'No (active or inactive) maintainer mentioned for %s' % filename)) + if all_maintainers and should_have_no_maintainer: + print('%s:%d:%d: %s' % (FILENAME, 0, 0, 'Please remove %s from the ignore list of %s' % (filename, sys.argv[0]))) + + +def main(): + """Main entry point.""" + try: + with open(FILENAME, 'rb') as f: + botmeta = yaml.safe_load(f) + except yaml.error.MarkedYAMLError as ex: + print('%s:%d:%d: YAML load failed: %s' % (FILENAME, ex.context_mark.line + + 1, ex.context_mark.column + 1, re.sub(r'\s+', ' ', str(ex)))) + return + except Exception as ex: # pylint: disable=broad-except + print('%s:%d:%d: YAML load failed: %s' % + (FILENAME, 0, 0, re.sub(r'\s+', ' ', str(ex)))) + return + + # Validate schema + + MacroSchema = Schema({ + (str): Any(str, None), + }, extra=PREVENT_EXTRA) + + FilesSchema = Schema({ + (str): { + ('supershipit'): str, + ('support'): Any('community'), + ('maintainers'): str, + ('labels'): str, + ('keywords'): str, + ('notify'): str, + ('ignore'): str, + }, + }, extra=PREVENT_EXTRA) + + schema = Schema({ + ('notifications'): bool, + ('automerge'): bool, + ('macros'): MacroSchema, + ('files'): FilesSchema, + }, extra=PREVENT_EXTRA) + + try: + schema(botmeta) + except MultipleInvalid as ex: + for error in ex.errors: + # No way to get line/column numbers + print('%s:%d:%d: %s' % (FILENAME, 0, 0, humanize_error(botmeta, error))) + return + + # Preprocess (substitute macros, convert to lists) + macros = botmeta.get('macros') or {} + macro_re = re.compile(r'\$([a-zA-Z_]+)') + + def convert_macros(text, macros): + def f(m): + macro = m.group(1) + replacement = (macros[macro] or '') + if macro == 'team_ansible_core': + return '$team_ansible_core %s' % replacement + return replacement + + return macro_re.sub(f, text) + + files = {} + try: + for file, filedata in (botmeta.get('files') or {}).items(): + file = convert_macros(file, macros) + filedata = dict((k, convert_macros(v, macros)) for k, v in filedata.items()) + files[file] = filedata + for k, v in filedata.items(): + if k in LIST_ENTRIES: + filedata[k] = v.split() + except KeyError as e: + print('%s:%d:%d: %s' % (FILENAME, 0, 0, 'Found unknown macro %s' % e)) + return + + # Scan all files + unmatched = set(files) + for dirs in ('plugins', 'tests', 'changelogs'): + for dirpath, dirnames, filenames in os.walk(dirs): + for file in sorted(filenames): + if file.endswith('.pyc'): + continue + filename = os.path.join(dirpath, file) + if os.path.islink(filename): + continue + if os.path.isfile(filename): + matching_files = [] + for file, filedata in files.items(): + if filename.startswith(file): + matching_files.append((file, filedata)) + if file in unmatched: + unmatched.remove(file) + if not matching_files: + print('%s:%d:%d: %s' % (FILENAME, 0, 0, 'Did not find any entry for %s' % filename)) + + matching_files.sort(key=lambda kv: kv[0]) + filedata = dict() + for k in LIST_ENTRIES: + filedata[k] = [] + for dummy, data in matching_files: + for k, v in data.items(): + if k in LIST_ENTRIES: + v = filedata[k] + v + filedata[k] = v + validate(filename, filedata) + + for file in unmatched: + print('%s:%d:%d: %s' % (FILENAME, 0, 0, 'Entry %s was not used' % file)) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/sanity/extra/extra-docs.json b/ansible_collections/community/general/tests/sanity/extra/extra-docs.json new file mode 100644 index 000000000..9a28d174f --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/extra-docs.json @@ -0,0 +1,13 @@ +{ + "include_symlinks": false, + "prefixes": [ + "docs/docsite/", + "plugins/", + "roles/" + ], + "output": "path-line-column-message", + "requirements": [ + "ansible-core", + "antsibull-docs" + ] +} diff --git a/ansible_collections/community/general/tests/sanity/extra/extra-docs.json.license b/ansible_collections/community/general/tests/sanity/extra/extra-docs.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/extra-docs.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/extra/extra-docs.py b/ansible_collections/community/general/tests/sanity/extra/extra-docs.py new file mode 100755 index 000000000..c636beb08 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/extra-docs.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# 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 +"""Check extra collection docs with antsibull-docs.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys +import subprocess + + +def main(): + """Main entry point.""" + env = os.environ.copy() + 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', '.'], + env=env, + check=False, + ) + if p.returncode not in (0, 3): + print('{0}:0:0: unexpected return code {1}'.format(sys.argv[0], p.returncode)) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/sanity/extra/licenses.json b/ansible_collections/community/general/tests/sanity/extra/licenses.json new file mode 100644 index 000000000..50e47ca88 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/licenses.json @@ -0,0 +1,4 @@ +{ + "include_symlinks": false, + "output": "path-message" +} diff --git a/ansible_collections/community/general/tests/sanity/extra/licenses.json.license b/ansible_collections/community/general/tests/sanity/extra/licenses.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/licenses.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/extra/licenses.py b/ansible_collections/community/general/tests/sanity/extra/licenses.py new file mode 100755 index 000000000..6227ee22f --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/licenses.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# Copyright (c) 2022, Felix Fontein +# 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 +"""Prevent files without a correct license identifier from being added to the source tree.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import glob +import sys + + +def format_license_list(licenses): + if not licenses: + return '(empty)' + return ', '.join(['"%s"' % license for license in licenses]) + + +def find_licenses(filename, relax=False): + spdx_license_identifiers = [] + other_license_identifiers = [] + has_copyright = False + try: + with open(filename, 'r', encoding='utf-8') as f: + for line in f: + line = line.rstrip() + if 'Copyright ' in line: + has_copyright = True + if 'Copyright: ' in line: + print('%s: found copyright line with "Copyright:". Please remove the colon.' % (filename, )) + if 'SPDX-FileCopyrightText: ' in line: + has_copyright = True + idx = line.find('SPDX-License-Identifier: ') + if idx >= 0: + lic_id = line[idx + len('SPDX-License-Identifier: '):] + spdx_license_identifiers.extend(lic_id.split(' OR ')) + if 'GNU General Public License' in line: + if 'v3.0+' in line: + other_license_identifiers.append('GPL-3.0-or-later') + if 'version 3 or later' in line: + other_license_identifiers.append('GPL-3.0-or-later') + if 'Simplified BSD License' in line: + other_license_identifiers.append('BSD-2-Clause') + if 'Apache License 2.0' in line: + other_license_identifiers.append('Apache-2.0') + if 'PSF License' in line or 'Python-2.0' in line: + other_license_identifiers.append('PSF-2.0') + if 'MIT License' in line: + other_license_identifiers.append('MIT') + except Exception as exc: + print('%s: error while processing file: %s' % (filename, exc)) + if len(set(spdx_license_identifiers)) < len(spdx_license_identifiers): + print('%s: found identical SPDX-License-Identifier values' % (filename, )) + if other_license_identifiers and set(other_license_identifiers) != set(spdx_license_identifiers): + print('%s: SPDX-License-Identifier yielded the license list %s, while manual guessing yielded the license list %s' % ( + filename, format_license_list(spdx_license_identifiers), format_license_list(other_license_identifiers))) + if not has_copyright and not relax: + print('%s: found no copyright notice' % (filename, )) + return sorted(spdx_license_identifiers) + + +def main(): + """Main entry point.""" + paths = sys.argv[1:] or sys.stdin.read().splitlines() + + # The following paths are allowed to have no license identifier + no_comments_allowed = [ + 'changelogs/fragments/*.yml', + 'changelogs/fragments/*.yaml', + ] + + # These files are completely ignored + ignore_paths = [ + '.ansible-test-timeout.json', + '.reuse/dep5', + 'LICENSES/*.txt', + 'COPYING', + ] + + no_comments_allowed = [fn for pattern in no_comments_allowed for fn in glob.glob(pattern)] + ignore_paths = [fn for pattern in ignore_paths for fn in glob.glob(pattern)] + + valid_licenses = [license_file[len('LICENSES/'):-len('.txt')] for license_file in glob.glob('LICENSES/*.txt')] + + for path in paths: + if path.startswith('./'): + path = path[2:] + if path in ignore_paths or path.startswith('tests/output/'): + continue + if os.stat(path).st_size == 0: + continue + if not path.endswith('.license') and os.path.exists(path + '.license'): + path = path + '.license' + valid_licenses_for_path = valid_licenses + if path.startswith('plugins/') and not path.startswith(('plugins/modules/', 'plugins/module_utils/', 'plugins/doc_fragments/')): + valid_licenses_for_path = [license for license in valid_licenses if license == 'GPL-3.0-or-later'] + licenses = find_licenses(path, relax=path in no_comments_allowed) + if not licenses: + if path not in no_comments_allowed: + print('%s: must have at least one license' % (path, )) + else: + for license in licenses: + if license not in valid_licenses_for_path: + print('%s: found not allowed license "%s", must be one of %s' % ( + path, license, format_license_list(valid_licenses_for_path))) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/sanity/extra/licenses.py.license b/ansible_collections/community/general/tests/sanity/extra/licenses.py.license new file mode 100644 index 000000000..6c4958feb --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/licenses.py.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, Felix Fontein diff --git a/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json b/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json new file mode 100644 index 000000000..c789a7fd3 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json @@ -0,0 +1,7 @@ +{ + "include_symlinks": true, + "prefixes": [ + "plugins/" + ], + "output": "path-message" +} diff --git a/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json.license b/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.py b/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.py new file mode 100755 index 000000000..b39df83a1 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/extra/no-unwanted-files.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# 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 +"""Prevent unwanted files from being added to the source tree.""" +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import os.path +import sys + + +def main(): + """Main entry point.""" + paths = sys.argv[1:] or sys.stdin.read().splitlines() + + allowed_extensions = ( + '.cs', + '.ps1', + '.psm1', + '.py', + ) + + skip_paths = set([ + ]) + + skip_directories = ( + ) + + yaml_directories = ( + 'plugins/test/', + 'plugins/filter/', + ) + + for path in paths: + if path in skip_paths: + continue + + if any(path.startswith(skip_directory) for skip_directory in skip_directories): + continue + + if os.path.islink(path): + print('%s: is a symbolic link' % (path, )) + elif not os.path.isfile(path): + print('%s: is not a regular file' % (path, )) + + ext = os.path.splitext(path)[1] + + if ext in ('.yml', ) and any(path.startswith(yaml_directory) for yaml_directory in yaml_directories): + continue + + if ext not in allowed_extensions: + print('%s: extension must be one of: %s' % (path, ', '.join(allowed_extensions))) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.11.txt b/ansible_collections/community/general/tests/sanity/ignore-2.11.txt new file mode 100644 index 000000000..f2c30270c --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.11.txt @@ -0,0 +1,28 @@ +.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.11.txt.license b/ansible_collections/community/general/tests/sanity/ignore-2.11.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.11.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.12.txt b/ansible_collections/community/general/tests/sanity/ignore-2.12.txt new file mode 100644 index 000000000..a8e04ff30 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.12.txt @@ -0,0 +1,21 @@ +.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.12.txt.license b/ansible_collections/community/general/tests/sanity/ignore-2.12.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.12.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.13.txt b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt new file mode 100644 index 000000000..a8e04ff30 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt @@ -0,0 +1,21 @@ +.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.license b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.14.txt b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt new file mode 100644 index 000000000..7e00143a6 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt @@ -0,0 +1,23 @@ +.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/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 diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.14.txt.license b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.15.txt b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt new file mode 100644 index 000000000..7e00143a6 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt @@ -0,0 +1,23 @@ +.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/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 diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.15.txt.license b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.16.txt b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt new file mode 100644 index 000000000..5fa3d90ba --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt @@ -0,0 +1,23 @@ +.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 # 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/xfconf.py validate-modules:return-syntax-error diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.16.txt.license b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/compat/__init__.py b/ansible_collections/community/general/tests/unit/compat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/compat/builtins.py b/ansible_collections/community/general/tests/unit/compat/builtins.py new file mode 100644 index 000000000..d548601d4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/compat/builtins.py @@ -0,0 +1,20 @@ +# Copyright (c) 2014, Toshio Kuratomi +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +# +# Compat for python2.7 +# + +# One unittest needs to import builtins via __import__() so we need to have +# the string that represents it +try: + import __builtin__ # noqa: F401, pylint: disable=unused-import +except ImportError: + BUILTINS = 'builtins' +else: + BUILTINS = '__builtin__' diff --git a/ansible_collections/community/general/tests/unit/compat/mock.py b/ansible_collections/community/general/tests/unit/compat/mock.py new file mode 100644 index 000000000..bdbea945e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/compat/mock.py @@ -0,0 +1,109 @@ +# Copyright (c) 2014, Toshio Kuratomi +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python3.x's unittest.mock module +''' +import sys + +# Python 2.7 + +# Note: Could use the pypi mock library on python3.x as well as python2.x. It +# is the same as the python3 stdlib mock library + +try: + # Allow wildcard import because we really do want to import all of mock's + # symbols into this compat shim + # pylint: disable=wildcard-import,unused-wildcard-import + from unittest.mock import * # noqa: F401, pylint: disable=unused-import +except ImportError: + # Python 2 + # pylint: disable=wildcard-import,unused-wildcard-import + try: + from mock import * # noqa: F401, pylint: disable=unused-import + except ImportError: + print('You need the mock library installed on python2.x to run tests') + + +# Prior to 3.4.4, mock_open cannot handle binary read_data +if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): + file_spec = None + + def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] + else: + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + + for line in data_as_list: + yield line + + def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` methoddline`, and `readlines` of the + file handle to return. This is an empty string by default. + """ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return type(read_data)().join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + global file_spec + if file_spec is None: + import _io + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + + mock.return_value = handle + return mock diff --git a/ansible_collections/community/general/tests/unit/compat/unittest.py b/ansible_collections/community/general/tests/unit/compat/unittest.py new file mode 100644 index 000000000..d50bab86f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/compat/unittest.py @@ -0,0 +1,25 @@ +# Copyright (c) 2014, Toshio Kuratomi +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python2.7's unittest module +''' + +import sys + +# Allow wildcard import because we really do want to import all of +# unittests's symbols into this compat shim +# pylint: disable=wildcard-import,unused-wildcard-import +if sys.version_info < (2, 7): + try: + # Need unittest2 on python2.6 + from unittest2 import * # noqa: F401, pylint: disable=unused-import + except ImportError: + print('You need unittest2 installed on python2.6.x to run tests') +else: + from unittest import * # noqa: F401, pylint: disable=unused-import diff --git a/ansible_collections/community/general/tests/unit/mock/loader.py b/ansible_collections/community/general/tests/unit/mock/loader.py new file mode 100644 index 000000000..948f4eecd --- /dev/null +++ b/ansible_collections/community/general/tests/unit/mock/loader.py @@ -0,0 +1,103 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# +# 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 + +from ansible.errors import AnsibleParserError +from ansible.parsing.dataloader import DataLoader +from ansible.module_utils.common.text.converters import to_bytes, to_text + + +class DictDataLoader(DataLoader): + + def __init__(self, file_mapping=None): + file_mapping = {} if file_mapping is None else file_mapping + assert type(file_mapping) == dict + + super(DictDataLoader, self).__init__() + + self._file_mapping = file_mapping + self._build_known_directories() + self._vault_secrets = None + + def load_from_file(self, path, cache=True, unsafe=False): + path = to_text(path) + if path in self._file_mapping: + return self.load(self._file_mapping[path], path) + return None + + # TODO: the real _get_file_contents returns a bytestring, so we actually convert the + # unicode/text it's created with to utf-8 + def _get_file_contents(self, file_name): + path = to_text(file_name) + if path in self._file_mapping: + return (to_bytes(self._file_mapping[path]), False) + else: + raise AnsibleParserError("file not found: %s" % path) + + def path_exists(self, path): + path = to_text(path) + return path in self._file_mapping or path in self._known_directories + + def is_file(self, path): + path = to_text(path) + return path in self._file_mapping + + def is_directory(self, path): + path = to_text(path) + return path in self._known_directories + + def list_directory(self, path): + ret = [] + path = to_text(path) + for x in (list(self._file_mapping.keys()) + self._known_directories): + if x.startswith(path): + if os.path.dirname(x) == path: + ret.append(os.path.basename(x)) + return ret + + def is_executable(self, path): + # FIXME: figure out a way to make paths return true for this + return False + + def _add_known_directory(self, directory): + if directory not in self._known_directories: + self._known_directories.append(directory) + + def _build_known_directories(self): + self._known_directories = [] + for path in self._file_mapping: + dirname = os.path.dirname(path) + while dirname not in ('/', ''): + self._add_known_directory(dirname) + dirname = os.path.dirname(dirname) + + def push(self, path, content): + rebuild_dirs = False + if path not in self._file_mapping: + rebuild_dirs = True + + self._file_mapping[path] = content + + if rebuild_dirs: + self._build_known_directories() + + def pop(self, path): + if path in self._file_mapping: + del self._file_mapping[path] + self._build_known_directories() + + def clear(self): + self._file_mapping = dict() + self._known_directories = [] + + def get_basedir(self): + return os.getcwd() + + def set_vault_secrets(self, vault_secrets): + self._vault_secrets = vault_secrets diff --git a/ansible_collections/community/general/tests/unit/mock/path.py b/ansible_collections/community/general/tests/unit/mock/path.py new file mode 100644 index 000000000..62ae02343 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/mock/path.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.community.general.tests.unit.compat.mock import MagicMock +from ansible.utils.path import unfrackpath + + +mock_unfrackpath_noop = MagicMock(spec_set=unfrackpath, side_effect=lambda x, *args, **kwargs: x) diff --git a/ansible_collections/community/general/tests/unit/mock/procenv.py b/ansible_collections/community/general/tests/unit/mock/procenv.py new file mode 100644 index 000000000..4646d7f35 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/mock/procenv.py @@ -0,0 +1,77 @@ +# Copyright (c) 2016, Matt Davis +# Copyright (c) 2016, Toshio Kuratomi +# +# 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 contextlib import contextmanager +from io import BytesIO, StringIO +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible.module_utils.six import PY3 +from ansible.module_utils.common.text.converters import to_bytes + + +@contextmanager +def swap_stdin_and_argv(stdin_data='', argv_data=tuple()): + """ + context manager that temporarily masks the test runner's values for stdin and argv + """ + real_stdin = sys.stdin + real_argv = sys.argv + + if PY3: + fake_stream = StringIO(stdin_data) + fake_stream.buffer = BytesIO(to_bytes(stdin_data)) + else: + fake_stream = BytesIO(to_bytes(stdin_data)) + + try: + sys.stdin = fake_stream + sys.argv = argv_data + + yield + finally: + sys.stdin = real_stdin + sys.argv = real_argv + + +@contextmanager +def swap_stdout(): + """ + context manager that temporarily replaces stdout for tests that need to verify output + """ + old_stdout = sys.stdout + + if PY3: + fake_stream = StringIO() + else: + fake_stream = BytesIO() + + try: + sys.stdout = fake_stream + + yield fake_stream + finally: + sys.stdout = old_stdout + + +class ModuleTestCase(unittest.TestCase): + def setUp(self, module_args=None): + if module_args is None: + module_args = {'_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False} + + args = json.dumps(dict(ANSIBLE_MODULE_ARGS=module_args)) + + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap = swap_stdin_and_argv(stdin_data=args) + self.stdin_swap.__enter__() + + def tearDown(self): + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap.__exit__(None, None, None) diff --git a/ansible_collections/community/general/tests/unit/mock/vault_helper.py b/ansible_collections/community/general/tests/unit/mock/vault_helper.py new file mode 100644 index 000000000..2b116129f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/mock/vault_helper.py @@ -0,0 +1,29 @@ +# 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.module_utils.common.text.converters import to_bytes + +from ansible.parsing.vault import VaultSecret + + +class TextVaultSecret(VaultSecret): + '''A secret piece of text. ie, a password. Tracks text encoding. + + The text encoding of the text may not be the default text encoding so + we keep track of the encoding so we encode it to the same bytes.''' + + def __init__(self, text, encoding=None, errors=None, _bytes=None): + super(TextVaultSecret, self).__init__() + self.text = text + self.encoding = encoding or 'utf-8' + self._bytes = _bytes + self.errors = errors or 'strict' + + @property + def bytes(self): + '''The text encoded with encoding, unless we specifically set _bytes.''' + return self._bytes or to_bytes(self.text, encoding=self.encoding, errors=self.errors) diff --git a/ansible_collections/community/general/tests/unit/mock/yaml_helper.py b/ansible_collections/community/general/tests/unit/mock/yaml_helper.py new file mode 100644 index 000000000..ce1bd719b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/mock/yaml_helper.py @@ -0,0 +1,128 @@ +# 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 io +import yaml + +from ansible.module_utils.six import PY3 +from ansible.parsing.yaml.loader import AnsibleLoader +from ansible.parsing.yaml.dumper import AnsibleDumper + + +class YamlTestUtils(object): + """Mixin class to combine with a unittest.TestCase subclass.""" + def _loader(self, stream): + """Vault related tests will want to override this. + + Vault cases should setup a AnsibleLoader that has the vault password.""" + return AnsibleLoader(stream) + + def _dump_stream(self, obj, stream, dumper=None): + """Dump to a py2-unicode or py3-string stream.""" + if PY3: + return yaml.dump(obj, stream, Dumper=dumper) + else: + return yaml.dump(obj, stream, Dumper=dumper, encoding=None) + + def _dump_string(self, obj, dumper=None): + """Dump to a py2-unicode or py3-string""" + if PY3: + return yaml.dump(obj, Dumper=dumper) + else: + return yaml.dump(obj, Dumper=dumper, encoding=None) + + def _dump_load_cycle(self, obj): + # Each pass though a dump or load revs the 'generation' + # obj to yaml string + string_from_object_dump = self._dump_string(obj, dumper=AnsibleDumper) + + # wrap a stream/file like StringIO around that yaml + stream_from_object_dump = io.StringIO(string_from_object_dump) + loader = self._loader(stream_from_object_dump) + # load the yaml stream to create a new instance of the object (gen 2) + obj_2 = loader.get_data() + + # dump the gen 2 objects directory to strings + string_from_object_dump_2 = self._dump_string(obj_2, + dumper=AnsibleDumper) + + # The gen 1 and gen 2 yaml strings + self.assertEqual(string_from_object_dump, string_from_object_dump_2) + # the gen 1 (orig) and gen 2 py object + self.assertEqual(obj, obj_2) + + # again! gen 3... load strings into py objects + stream_3 = io.StringIO(string_from_object_dump_2) + loader_3 = self._loader(stream_3) + obj_3 = loader_3.get_data() + + string_from_object_dump_3 = self._dump_string(obj_3, dumper=AnsibleDumper) + + self.assertEqual(obj, obj_3) + # should be transitive, but... + self.assertEqual(obj_2, obj_3) + self.assertEqual(string_from_object_dump, string_from_object_dump_3) + + def _old_dump_load_cycle(self, obj): + '''Dump the passed in object to yaml, load it back up, dump again, compare.''' + stream = io.StringIO() + + yaml_string = self._dump_string(obj, dumper=AnsibleDumper) + self._dump_stream(obj, stream, dumper=AnsibleDumper) + + yaml_string_from_stream = stream.getvalue() + + # reset stream + stream.seek(0) + + loader = self._loader(stream) + # loader = AnsibleLoader(stream, vault_password=self.vault_password) + obj_from_stream = loader.get_data() + + stream_from_string = io.StringIO(yaml_string) + loader2 = self._loader(stream_from_string) + # loader2 = AnsibleLoader(stream_from_string, vault_password=self.vault_password) + obj_from_string = loader2.get_data() + + stream_obj_from_stream = io.StringIO() + stream_obj_from_string = io.StringIO() + + if PY3: + yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper) + yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper) + else: + yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper, encoding=None) + yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper, encoding=None) + + yaml_string_stream_obj_from_stream = stream_obj_from_stream.getvalue() + yaml_string_stream_obj_from_string = stream_obj_from_string.getvalue() + + stream_obj_from_stream.seek(0) + stream_obj_from_string.seek(0) + + if PY3: + yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper) + yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper) + else: + yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper, encoding=None) + yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper, encoding=None) + + assert yaml_string == yaml_string_obj_from_stream + assert yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + assert (yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string == yaml_string_stream_obj_from_stream == + yaml_string_stream_obj_from_string) + assert obj == obj_from_stream + assert obj == obj_from_string + assert obj == yaml_string_obj_from_stream + assert obj == yaml_string_obj_from_string + assert obj == obj_from_stream == obj_from_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + return {'obj': obj, + 'yaml_string': yaml_string, + 'yaml_string_from_stream': yaml_string_from_stream, + 'obj_from_stream': obj_from_stream, + 'obj_from_string': obj_from_string, + 'yaml_string_obj_from_string': yaml_string_obj_from_string} diff --git a/ansible_collections/community/general/tests/unit/plugins/become/conftest.py b/ansible_collections/community/general/tests/unit/plugins/become/conftest.py new file mode 100644 index 000000000..93b593bdf --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/conftest.py @@ -0,0 +1,38 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# Copyright (c) 2017 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible.cli.arguments import option_helpers as opt_help +from ansible.utils import context_objects as co + + +@pytest.fixture +def parser(): + parser = opt_help.create_base_parser('testparser') + + opt_help.add_runas_options(parser) + opt_help.add_meta_options(parser) + opt_help.add_runtask_options(parser) + opt_help.add_vault_options(parser) + opt_help.add_async_options(parser) + opt_help.add_connect_options(parser) + opt_help.add_subset_options(parser) + opt_help.add_check_options(parser) + opt_help.add_inventory_options(parser) + + return parser + + +@pytest.fixture +def reset_cli_args(): + co.GlobalCLIArgs._Singleton__instance = None + yield + co.GlobalCLIArgs._Singleton__instance = None diff --git a/ansible_collections/community/general/tests/unit/plugins/become/helper.py b/ansible_collections/community/general/tests/unit/plugins/become/helper.py new file mode 100644 index 000000000..9949e1bef --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/helper.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012-2014, Michael DeHaan +# +# 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.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.""" + plugin = become_loader.get(task['become_method']) + plugin.set_options(task_keys=task, var_options=var_options) + shell = get_shell_plugin(executable=executable) + return plugin.build_become_command(cmd, shell) diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_doas.py b/ansible_collections/community/general/tests/unit/plugins/become/test_doas.py new file mode 100644 index 000000000..4a922e9f2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_doas.py @@ -0,0 +1,85 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# Copyright (c) 2020 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_doas_basic(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + doas_exe = 'doas' + doas_flags = '-n' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_method': 'community.general.doas', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s %s -c 'echo %s; %s'""" % (doas_exe, doas_flags, default_exe, success, + default_cmd), cmd) is not None) + + +def test_doas(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + doas_exe = 'doas' + doas_flags = '-n' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.doas', + 'become_flags': doas_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s -u %s %s -c 'echo %s; %s'""" % (doas_exe, doas_flags, task['become_user'], default_exe, success, + default_cmd), cmd) is not None) + + +def test_doas_varoptions(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + doas_exe = 'doas' + doas_flags = '-n' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.doas', + 'become_flags': 'xxx', + } + var_options = { + 'ansible_become_user': 'bar', + 'ansible_become_flags': doas_flags, + } + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s -u %s %s -c 'echo %s; %s'""" % (doas_exe, doas_flags, var_options['ansible_become_user'], default_exe, success, + default_cmd), cmd) is not None) diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_dzdo.py b/ansible_collections/community/general/tests/unit/plugins/become/test_dzdo.py new file mode 100644 index 000000000..24af2b50c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_dzdo.py @@ -0,0 +1,95 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# Copyright (c) 2020 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_dzdo_basic(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + dzdo_exe = 'dzdo' + dzdo_flags = '-H -S -n' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_method': 'community.general.dzdo', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s %s -c 'echo %s; %s'""" % (dzdo_exe, dzdo_flags, default_exe, + success, default_cmd), cmd) is not None + + +def test_dzdo(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + dzdo_exe = 'dzdo' + dzdo_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.dzdo', + 'become_flags': dzdo_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s -u %s %s -c 'echo %s; %s'""" % (dzdo_exe, dzdo_flags, task['become_user'], default_exe, + success, default_cmd), cmd) is not None + task['become_pass'] = 'testpass' + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s -p %s -u %s %s -c 'echo %s; %s'""" % (dzdo_exe, dzdo_flags, r'\"\[dzdo via ansible, key=.+?\] password:\"', + task['become_user'], default_exe, success, default_cmd), cmd) is not None + + +def test_dzdo_varoptions(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + dzdo_exe = 'dzdo' + dzdo_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.dzdo', + 'become_flags': 'xxx', + } + var_options = { + 'ansible_become_user': 'bar', + 'ansible_become_flags': dzdo_flags, + } + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s -u %s %s -c 'echo %s; %s'""" % (dzdo_exe, dzdo_flags, var_options['ansible_become_user'], default_exe, + success, default_cmd), cmd) is not None + var_options['ansible_become_pass'] = 'testpass' + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s -p %s -u %s %s -c 'echo %s; %s'""" % (dzdo_exe, dzdo_flags, r'\"\[dzdo via ansible, key=.+?\] password:\"', + var_options['ansible_become_user'], default_exe, success, default_cmd), cmd) is not None diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_ksu.py b/ansible_collections/community/general/tests/unit/plugins/become/test_ksu.py new file mode 100644 index 000000000..3ec171661 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_ksu.py @@ -0,0 +1,86 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# Copyright (c) 2020 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_ksu_basic(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + ksu_exe = 'ksu' + ksu_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.ksu', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s %s -e %s -c 'echo %s; %s'""" % (ksu_exe, task['become_user'], ksu_flags, + default_exe, success, default_cmd), cmd) is not None) + + +def test_ksu(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + ksu_exe = 'ksu' + ksu_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.ksu', + 'become_flags': ksu_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s %s -e %s -c 'echo %s; %s'""" % (ksu_exe, task['become_user'], ksu_flags, + default_exe, success, default_cmd), cmd) is not None) + + +def test_ksu_varoptions(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + ksu_exe = 'ksu' + ksu_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.ksu', + 'become_flags': 'xxx', + } + var_options = { + 'ansible_become_user': 'bar', + 'ansible_become_flags': ksu_flags, + } + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s %s -e %s -c 'echo %s; %s'""" % (ksu_exe, var_options['ansible_become_user'], ksu_flags, + default_exe, success, default_cmd), cmd) is not None) diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_pbrun.py b/ansible_collections/community/general/tests/unit/plugins/become/test_pbrun.py new file mode 100644 index 000000000..eceea2e66 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_pbrun.py @@ -0,0 +1,85 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# Copyright (c) 2020 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_pbrun_basic(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + pbrun_exe = 'pbrun' + pbrun_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_method': 'community.general.pbrun', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s 'echo %s; %s'""" % (pbrun_exe, pbrun_flags, + success, default_cmd), cmd) is not None + + +def test_pbrun(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + pbrun_exe = 'pbrun' + pbrun_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.pbrun', + 'become_flags': pbrun_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s -u %s 'echo %s; %s'""" % (pbrun_exe, pbrun_flags, task['become_user'], + success, default_cmd), cmd) is not None + + +def test_pbrun_var_varoptions(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + pbrun_exe = 'pbrun' + pbrun_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.pbrun', + 'become_flags': 'xxx', + } + var_options = { + 'ansible_become_user': 'bar', + 'ansible_become_flags': pbrun_flags, + } + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s -u %s 'echo %s; %s'""" % (pbrun_exe, pbrun_flags, var_options['ansible_become_user'], + success, default_cmd), cmd) is not None diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_pfexec.py b/ansible_collections/community/general/tests/unit/plugins/become/test_pfexec.py new file mode 100644 index 000000000..350cd5ad2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_pfexec.py @@ -0,0 +1,82 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# Copyright (c) 2020 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_pfexec_basic(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + pfexec_exe = 'pfexec' + pfexec_flags = '-H -S -n' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_method': 'community.general.pfexec', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s 'echo %s; %s'""" % (pfexec_exe, pfexec_flags, success, default_cmd), cmd) is not None + + +def test_pfexec(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + pfexec_exe = 'pfexec' + pfexec_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.pfexec', + 'become_flags': pfexec_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s 'echo %s; %s'""" % (pfexec_exe, pfexec_flags, success, default_cmd), cmd) is not None + + +def test_pfexec_varoptions(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + pfexec_exe = 'pfexec' + pfexec_flags = '' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.pfexec', + 'become_flags': 'xxx', + } + var_options = { + 'ansible_become_user': 'bar', + 'ansible_become_flags': pfexec_flags, + } + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert re.match("""%s %s 'echo %s; %s'""" % (pfexec_exe, pfexec_flags, success, default_cmd), cmd) is not None diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_sudosu.py b/ansible_collections/community/general/tests/unit/plugins/become/test_sudosu.py new file mode 100644 index 000000000..f63f48df7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_sudosu.py @@ -0,0 +1,51 @@ +# Copyright (c) 2012-2014, Michael DeHaan +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_sudosu(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/bash" + sudo_exe = 'sudo' + sudo_flags = '-H -s -n' + + success = 'BECOME-SUCCESS-.+?' + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.sudosu', + 'become_flags': sudo_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s su -l %s %s -c 'echo %s; %s'""" % (sudo_exe, sudo_flags, task['become_user'], + default_exe, success, default_cmd), cmd) is not None) + + task = { + 'become_user': 'foo', + 'become_method': 'community.general.sudosu', + 'become_flags': sudo_flags, + 'become_pass': 'testpass', + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + print(cmd) + assert (re.match("""%s %s -p "%s" su -l %s %s -c 'echo %s; %s'""" % (sudo_exe, sudo_flags.replace('-n', ''), + r"\[sudo via ansible, key=.+?\] password:", task['become_user'], + default_exe, success, default_cmd), cmd) is not None) diff --git a/ansible_collections/community/general/tests/unit/plugins/cache/test_memcached.py b/ansible_collections/community/general/tests/unit/plugins/cache/test_memcached.py new file mode 100644 index 000000000..8e203cfab --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/cache/test_memcached.py @@ -0,0 +1,18 @@ +# Copyright (c) 2012-2015, Michael DeHaan +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +pytest.importorskip('memcache') + +from ansible.plugins.loader import cache_loader +from ansible_collections.community.general.plugins.cache.memcached import CacheModule as MemcachedCache + + +def test_memcached_cachemodule(): + assert isinstance(cache_loader.get('community.general.memcached'), MemcachedCache) diff --git a/ansible_collections/community/general/tests/unit/plugins/cache/test_redis.py b/ansible_collections/community/general/tests/unit/plugins/cache/test_redis.py new file mode 100644 index 000000000..81ae9293e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/cache/test_redis.py @@ -0,0 +1,26 @@ +# Copyright (c) 2012-2015, Michael DeHaan +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +pytest.importorskip('redis') + +from ansible.plugins.loader import cache_loader +from ansible_collections.community.general.plugins.cache.redis import CacheModule as RedisCache + + +def test_redis_cachemodule(): + # The _uri option is required for the redis plugin + connection = '127.0.0.1:6379:1' + assert isinstance(cache_loader.get('community.general.redis', **{'_uri': connection}), RedisCache) + + +def test_redis_cachemodule(): + # The _uri option is required for the redis plugin + connection = '[::1]:6379:1' + assert isinstance(cache_loader.get('community.general.redis', **{'_uri': connection}), RedisCache) diff --git a/ansible_collections/community/general/tests/unit/plugins/callback/test_elastic.py b/ansible_collections/community/general/tests/unit/plugins/callback/test_elastic.py new file mode 100644 index 000000000..73f4a6c27 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/callback/test_elastic.py @@ -0,0 +1,127 @@ +# Copyright (c) 2021, Victor Martinez +# 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.playbook.task import Task +from ansible.executor.task_result import TaskResult +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.tests.unit.compat.mock import patch, MagicMock, Mock +from ansible_collections.community.general.plugins.callback.elastic import ElasticSource, TaskData +from collections import OrderedDict +import sys + +ELASTIC_MINIMUM_PYTHON_VERSION = (3, 6) + + +class TestOpentelemetry(unittest.TestCase): + @patch('ansible_collections.community.general.plugins.callback.elastic.socket') + def setUp(self, mock_socket): + if sys.version_info < ELASTIC_MINIMUM_PYTHON_VERSION: + self.skipTest("Python %s+ is needed for Elastic" % + ",".join(map(str, ELASTIC_MINIMUM_PYTHON_VERSION))) + mock_socket.gethostname.return_value = 'my-host' + mock_socket.gethostbyname.return_value = '1.2.3.4' + self.elastic = ElasticSource(display=None) + self.task_fields = {'args': {}} + self.mock_host = Mock('MockHost') + self.mock_host.name = 'myhost' + self.mock_host._uuid = 'myhost_uuid' + self.mock_task = Task() + self.mock_task.action = 'myaction' + self.mock_task.no_log = False + self.mock_task._role = 'myrole' + self.mock_task._uuid = 'myuuid' + self.mock_task.args = {} + self.mock_task.get_name = MagicMock(return_value='mytask') + self.mock_task.get_path = MagicMock(return_value='/mypath') + self.my_task = TaskData('myuuid', 'mytask', '/mypath', 'myplay', 'myaction', '') + self.my_task_result = TaskResult(host=self.mock_host, task=self.mock_task, return_data={}, task_fields=self.task_fields) + + def test_start_task(self): + tasks_data = OrderedDict() + + self.elastic.start_task( + tasks_data, + False, + 'myplay', + self.mock_task + ) + + task_data = tasks_data['myuuid'] + self.assertEqual(task_data.uuid, 'myuuid') + self.assertEqual(task_data.name, 'mytask') + self.assertEqual(task_data.path, '/mypath') + self.assertEqual(task_data.play, 'myplay') + self.assertEqual(task_data.action, 'myaction') + self.assertEqual(task_data.args, '') + + def test_finish_task_with_a_host_match(self): + tasks_data = OrderedDict() + tasks_data['myuuid'] = self.my_task + + self.elastic.finish_task( + tasks_data, + 'ok', + self.my_task_result + ) + + task_data = tasks_data['myuuid'] + host_data = task_data.host_data['myhost_uuid'] + self.assertEqual(host_data.uuid, 'myhost_uuid') + self.assertEqual(host_data.name, 'myhost') + self.assertEqual(host_data.status, 'ok') + + def test_finish_task_without_a_host_match(self): + result = TaskResult(host=None, task=self.mock_task, return_data={}, task_fields=self.task_fields) + tasks_data = OrderedDict() + tasks_data['myuuid'] = self.my_task + + self.elastic.finish_task( + tasks_data, + 'ok', + result + ) + + task_data = tasks_data['myuuid'] + host_data = task_data.host_data['include'] + self.assertEqual(host_data.uuid, 'include') + self.assertEqual(host_data.name, 'include') + self.assertEqual(host_data.status, 'ok') + + def test_get_error_message(self): + test_cases = ( + ('my-exception', 'my-msg', None, 'my-exception'), + (None, 'my-msg', None, 'my-msg'), + (None, None, None, 'failed'), + ) + + for tc in test_cases: + result = self.elastic.get_error_message(generate_test_data(tc[0], tc[1], tc[2])) + self.assertEqual(result, tc[3]) + + def test_enrich_error_message(self): + test_cases = ( + ('my-exception', 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"'), + ('my-exception', None, 'my-stderr', 'message: "failed"\nexception: "my-exception"\nstderr: "my-stderr"'), + (None, 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "None"\nstderr: "my-stderr"'), + ('my-exception', 'my-msg', None, 'message: "my-msg"\nexception: "my-exception"\nstderr: "None"'), + ('my-exception', 'my-msg', '\nline1\nline2', 'message: "my-msg"\nexception: "my-exception"\nstderr: "\nline1\nline2"') + ) + + for tc in test_cases: + result = self.elastic.enrich_error_message(generate_test_data(tc[0], tc[1], tc[2])) + self.assertEqual(result, tc[3]) + + +def generate_test_data(exception=None, msg=None, stderr=None): + res_data = OrderedDict() + if exception: + res_data['exception'] = exception + if msg: + res_data['msg'] = msg + if stderr: + res_data['stderr'] = stderr + return res_data 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 new file mode 100644 index 000000000..f9fef3c5d --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.executor.task_result import TaskResult +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.tests.unit.compat.mock import patch, Mock +from ansible_collections.community.general.plugins.callback.loganalytics import AzureLogAnalyticsSource +from datetime import datetime + +import json + + +class TestAzureLogAnalytics(unittest.TestCase): + @patch('ansible_collections.community.general.plugins.callback.loganalytics.socket') + def setUp(self, mock_socket): + mock_socket.gethostname.return_value = 'my-host' + mock_socket.gethostbyname.return_value = '1.2.3.4' + self.loganalytics = AzureLogAnalyticsSource() + self.mock_task = Mock('MockTask') + self.mock_task._role = 'myrole' + self.mock_task._uuid = 'myuuid' + self.task_fields = {'args': {}} + self.mock_host = Mock('MockHost') + self.mock_host.name = 'myhost' + + @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): + mock_datetime.utcnow.return_value = datetime(2020, 12, 1) + result = TaskResult(host=self.mock_host, task=self.mock_task, return_data={}, task_fields=self.task_fields) + + self.loganalytics.send_event(workspace_id='01234567-0123-0123-0123-01234567890a', + shared_key='dZD0kCbKl3ehZG6LHFMuhtE0yHiFCmetzFMc2u+roXIUQuatqU924SsAAAAPemhjbGlAemhjbGktTUJQAQIDBA==', + state='OK', + result=result, + runtime=100) + + args, kwargs = open_url_mock.call_args + sent_data = json.loads(args[1]) + + self.assertEqual(sent_data['event']['timestamp'], 'Tue, 01 Dec 2020 00:00:00 GMT') + self.assertEqual(sent_data['event']['host'], 'my-host') + self.assertEqual(sent_data['event']['uuid'], 'myuuid') + self.assertEqual(args[0], 'https://01234567-0123-0123-0123-01234567890a.ods.opinsights.azure.com/api/logs?api-version=2016-04-01') + + @patch('ansible_collections.community.general.plugins.callback.loganalytics.datetime') + @patch('ansible_collections.community.general.plugins.callback.loganalytics.open_url') + def test_auth_headers(self, open_url_mock, mock_datetime): + mock_datetime.utcnow.return_value = datetime(2020, 12, 1) + result = TaskResult(host=self.mock_host, task=self.mock_task, return_data={}, task_fields=self.task_fields) + + self.loganalytics.send_event(workspace_id='01234567-0123-0123-0123-01234567890a', + shared_key='dZD0kCbKl3ehZG6LHFMuhtE0yHiFCmetzFMc2u+roXIUQuatqU924SsAAAAPemhjbGlAemhjbGktTUJQAQIDBA==', + state='OK', + result=result, + runtime=100) + + args, kwargs = open_url_mock.call_args + headers = kwargs['headers'] + + self.assertRegexpMatches(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/callback/test_opentelemetry.py b/ansible_collections/community/general/tests/unit/plugins/callback/test_opentelemetry.py new file mode 100644 index 000000000..dea2e29d4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/callback/test_opentelemetry.py @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Victor Martinez +# 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.playbook.task import Task +from ansible.executor.task_result import TaskResult +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.tests.unit.compat.mock import patch, MagicMock, Mock +from ansible_collections.community.general.plugins.callback.opentelemetry import OpenTelemetrySource, TaskData +from collections import OrderedDict +import sys + +OPENTELEMETRY_MINIMUM_PYTHON_VERSION = (3, 7) + + +class TestOpentelemetry(unittest.TestCase): + @patch('ansible_collections.community.general.plugins.callback.opentelemetry.socket') + def setUp(self, mock_socket): + # TODO: this python version validation won't be needed as long as the _time_ns call is mocked. + if sys.version_info < OPENTELEMETRY_MINIMUM_PYTHON_VERSION: + self.skipTest("Python %s+ is needed for OpenTelemetry" % + ",".join(map(str, OPENTELEMETRY_MINIMUM_PYTHON_VERSION))) + + mock_socket.gethostname.return_value = 'my-host' + mock_socket.gethostbyname.return_value = '1.2.3.4' + self.opentelemetry = OpenTelemetrySource(display=None) + self.task_fields = {'args': {}} + self.mock_host = Mock('MockHost') + self.mock_host.name = 'myhost' + self.mock_host._uuid = 'myhost_uuid' + self.mock_task = Task() + self.mock_task.action = 'myaction' + self.mock_task.no_log = False + self.mock_task._role = 'myrole' + self.mock_task._uuid = 'myuuid' + self.mock_task.args = {} + self.mock_task.get_name = MagicMock(return_value='mytask') + self.mock_task.get_path = MagicMock(return_value='/mypath') + self.my_task = TaskData('myuuid', 'mytask', '/mypath', 'myplay', 'myaction', '') + self.my_task_result = TaskResult(host=self.mock_host, task=self.mock_task, return_data={}, task_fields=self.task_fields) + + def test_start_task(self): + tasks_data = OrderedDict() + + self.opentelemetry.start_task( + tasks_data, + False, + 'myplay', + self.mock_task + ) + + task_data = tasks_data['myuuid'] + self.assertEqual(task_data.uuid, 'myuuid') + self.assertEqual(task_data.name, 'mytask') + self.assertEqual(task_data.path, '/mypath') + self.assertEqual(task_data.play, 'myplay') + self.assertEqual(task_data.action, 'myaction') + self.assertEqual(task_data.args, {}) + + def test_finish_task_with_a_host_match(self): + tasks_data = OrderedDict() + tasks_data['myuuid'] = self.my_task + + self.opentelemetry.finish_task( + tasks_data, + 'ok', + self.my_task_result, + "" + ) + + task_data = tasks_data['myuuid'] + host_data = task_data.host_data['myhost_uuid'] + self.assertEqual(host_data.uuid, 'myhost_uuid') + self.assertEqual(host_data.name, 'myhost') + self.assertEqual(host_data.status, 'ok') + + def test_finish_task_without_a_host_match(self): + result = TaskResult(host=None, task=self.mock_task, return_data={}, task_fields=self.task_fields) + tasks_data = OrderedDict() + tasks_data['myuuid'] = self.my_task + + self.opentelemetry.finish_task( + tasks_data, + 'ok', + result, + "" + ) + + task_data = tasks_data['myuuid'] + host_data = task_data.host_data['include'] + self.assertEqual(host_data.uuid, 'include') + self.assertEqual(host_data.name, 'include') + self.assertEqual(host_data.status, 'ok') + self.assertEqual(self.opentelemetry.ansible_version, None) + + def test_finish_task_include_with_ansible_version(self): + task_fields = {'args': {'_ansible_version': '1.2.3'}} + result = TaskResult(host=None, task=self.mock_task, return_data={}, task_fields=task_fields) + tasks_data = OrderedDict() + tasks_data['myuuid'] = self.my_task + + self.opentelemetry.finish_task( + tasks_data, + 'ok', + result, + "" + ) + + self.assertEqual(self.opentelemetry.ansible_version, '1.2.3') + + def test_get_error_message(self): + test_cases = ( + ('my-exception', 'my-msg', None, 'my-exception'), + (None, 'my-msg', None, 'my-msg'), + (None, None, None, 'failed'), + ) + + for tc in test_cases: + result = self.opentelemetry.get_error_message(generate_test_data(tc[0], tc[1], tc[2])) + self.assertEqual(result, tc[3]) + + def test_get_error_message_from_results(self): + test_cases = ( + ('my-exception', 'my-msg', None, False, None), + (None, 'my-msg', None, False, None), + (None, None, None, False, None), + ('my-exception', 'my-msg', None, True, 'shell(none) - my-exception'), + (None, 'my-msg', None, True, 'shell(none) - my-msg'), + (None, None, None, True, 'shell(none) - failed'), + ) + + for tc in test_cases: + result = self.opentelemetry.get_error_message_from_results([generate_test_data(tc[0], tc[1], tc[2], tc[3])], 'shell') + self.assertEqual(result, tc[4]) + + def test_enrich_error_message(self): + test_cases = ( + ('my-exception', 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"'), + ('my-exception', None, 'my-stderr', 'message: "failed"\nexception: "my-exception"\nstderr: "my-stderr"'), + (None, 'my-msg', 'my-stderr', 'message: "my-msg"\nexception: "None"\nstderr: "my-stderr"'), + ('my-exception', 'my-msg', None, 'message: "my-msg"\nexception: "my-exception"\nstderr: "None"'), + ('my-exception', 'my-msg', '\nline1\nline2', 'message: "my-msg"\nexception: "my-exception"\nstderr: "\nline1\nline2"') + ) + + for tc in test_cases: + result = self.opentelemetry.enrich_error_message(generate_test_data(tc[0], tc[1], tc[2])) + self.assertEqual(result, tc[3]) + + def test_enrich_error_message_from_results(self): + test_cases = ( + ('my-exception', 'my-msg', 'my-stderr', False, ''), + ('my-exception', None, 'my-stderr', False, ''), + (None, 'my-msg', 'my-stderr', False, ''), + ('my-exception', 'my-msg', None, False, ''), + ('my-exception', 'my-msg', '\nline1\nline2', False, ''), + ('my-exception', 'my-msg', 'my-stderr', True, 'shell(none) - message: "my-msg"\nexception: "my-exception"\nstderr: "my-stderr"\n'), + ('my-exception', None, 'my-stderr', True, 'shell(none) - message: "failed"\nexception: "my-exception"\nstderr: "my-stderr"\n'), + (None, 'my-msg', 'my-stderr', True, 'shell(none) - message: "my-msg"\nexception: "None"\nstderr: "my-stderr"\n'), + ('my-exception', 'my-msg', None, True, 'shell(none) - message: "my-msg"\nexception: "my-exception"\nstderr: "None"\n'), + ('my-exception', 'my-msg', '\nline1\nline2', True, 'shell(none) - message: "my-msg"\nexception: "my-exception"\nstderr: "\nline1\nline2"\n') + ) + + for tc in test_cases: + result = self.opentelemetry.enrich_error_message_from_results([generate_test_data(tc[0], tc[1], tc[2], tc[3])], 'shell') + self.assertEqual(result, tc[4]) + + def test_url_from_args(self): + test_cases = ( + ({}, ""), + ({'url': 'my-url'}, 'my-url'), + ({'url': 'my-url', 'api_url': 'my-api_url'}, 'my-url'), + ({'api_url': 'my-api_url'}, 'my-api_url'), + ({'api_url': 'my-api_url', 'chart_repo_url': 'my-chart_repo_url'}, 'my-api_url') + ) + + for tc in test_cases: + result = self.opentelemetry.url_from_args(tc[0]) + self.assertEqual(result, tc[1]) + + def test_parse_and_redact_url_if_possible(self): + test_cases = ( + ({}, None), + ({'url': 'wrong'}, None), + ({'url': 'https://my-url'}, 'https://my-url'), + ({'url': 'https://user:pass@my-url'}, 'https://my-url'), + ({'url': 'https://my-url:{{ my_port }}'}, 'https://my-url:{{ my_port }}'), + ({'url': 'https://{{ my_hostname }}:{{ my_port }}'}, None), + ({'url': '{{my_schema}}{{ my_hostname }}:{{ my_port }}'}, None) + ) + + for tc in test_cases: + result = self.opentelemetry.parse_and_redact_url_if_possible(tc[0]) + if tc[1]: + self.assertEqual(result.geturl(), tc[1]) + else: + self.assertEqual(result, tc[1]) + + +def generate_test_data(exception=None, msg=None, stderr=None, failed=False): + res_data = OrderedDict() + if exception: + res_data['exception'] = exception + if msg: + res_data['msg'] = msg + if stderr: + res_data['stderr'] = stderr + res_data['failed'] = failed + return res_data diff --git a/ansible_collections/community/general/tests/unit/plugins/callback/test_splunk.py b/ansible_collections/community/general/tests/unit/plugins/callback/test_splunk.py new file mode 100644 index 000000000..ddcdae24c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/callback/test_splunk.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.executor.task_result import TaskResult +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.tests.unit.compat.mock import patch, Mock +from ansible_collections.community.general.plugins.callback.splunk import SplunkHTTPCollectorSource +from datetime import datetime + +import json + + +class TestSplunkClient(unittest.TestCase): + @patch('ansible_collections.community.general.plugins.callback.splunk.socket') + def setUp(self, mock_socket): + mock_socket.gethostname.return_value = 'my-host' + mock_socket.gethostbyname.return_value = '1.2.3.4' + self.splunk = SplunkHTTPCollectorSource() + self.mock_task = Mock('MockTask') + self.mock_task._role = 'myrole' + self.mock_task._uuid = 'myuuid' + self.task_fields = {'args': {}} + self.mock_host = Mock('MockHost') + self.mock_host.name = 'myhost' + + @patch('ansible_collections.community.general.plugins.callback.splunk.datetime') + @patch('ansible_collections.community.general.plugins.callback.splunk.open_url') + def test_timestamp_with_milliseconds(self, open_url_mock, mock_datetime): + mock_datetime.utcnow.return_value = datetime(2020, 12, 1) + result = TaskResult(host=self.mock_host, task=self.mock_task, return_data={}, task_fields=self.task_fields) + + self.splunk.send_event( + url='endpoint', authtoken='token', validate_certs=False, include_milliseconds=True, + batch="abcefghi-1234-5678-9012-abcdefghijkl", state='OK', result=result, runtime=100 + ) + + args, kwargs = open_url_mock.call_args + sent_data = json.loads(args[1]) + + self.assertEqual(sent_data['event']['timestamp'], '2020-12-01 00:00:00.000000 +0000') + self.assertEqual(sent_data['event']['host'], 'my-host') + self.assertEqual(sent_data['event']['ip_address'], '1.2.3.4') + + @patch('ansible_collections.community.general.plugins.callback.splunk.datetime') + @patch('ansible_collections.community.general.plugins.callback.splunk.open_url') + def test_timestamp_without_milliseconds(self, open_url_mock, mock_datetime): + mock_datetime.utcnow.return_value = datetime(2020, 12, 1) + result = TaskResult(host=self.mock_host, task=self.mock_task, return_data={}, task_fields=self.task_fields) + + self.splunk.send_event( + url='endpoint', authtoken='token', validate_certs=False, include_milliseconds=False, + batch="abcefghi-1234-5678-9012-abcdefghijkl", state='OK', result=result, runtime=100 + ) + + args, kwargs = open_url_mock.call_args + sent_data = json.loads(args[1]) + + self.assertEqual(sent_data['event']['timestamp'], '2020-12-01 00:00:00 +0000') + self.assertEqual(sent_data['event']['host'], 'my-host') + self.assertEqual(sent_data['event']['ip_address'], '1.2.3.4') 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 new file mode 100644 index 000000000..8733a92e0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020 Red Hat Inc. +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +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.playbook.play_context import PlayContext + + +class TestLXCConnectionClass(unittest.TestCase): + + def test_lxc_connection_module(self): + play_context = PlayContext() + play_context.prompt = ( + '[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: ' + ) + in_stream = StringIO() + + self.assertIsInstance(lxc.Connection(play_context, in_stream), lxc.Connection) diff --git a/ansible_collections/community/general/tests/unit/plugins/filter/test_crc32.py b/ansible_collections/community/general/tests/unit/plugins/filter/test_crc32.py new file mode 100644 index 000000000..820104513 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/filter/test_crc32.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Julien Riou +# 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.plugins.filter.crc32 import crc32s + + +class TestFilterCrc32(unittest.TestCase): + + def test_checksum(self): + self.assertEqual(crc32s('test'), 'd87f7e0c') diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd new file mode 100644 index 000000000..dd6baa885 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd @@ -0,0 +1,174 @@ +{ + "instances":{ + "vlantest":{ + "instances":{ + "metadata":{ + "config":{ + "image.os":"ubuntu", + "image.release":"focal", + "image.version":"20.04", + "volatile.last_state.power":"RUNNING" + }, + "devices":{ + "eth0":{ + "name":"eth0", + "network":"my-macvlan", + "type":"nic" + } + }, + "profiles":[ + "default" + ], + "expanded_devices":{ + "eth0":{ + "name":"eth0", + "network":"my-macvlan", + "type":"nic" + } + }, + "name":"vlantest", + "status":"Running", + "location":"Berlin" + } + }, + "state":{ + "metadata":{ + "status":"Running", + "network":{ + "eth0":{ + "addresses":[ + { + "family":"inet", + "address":"10.98.143.199", + "netmask":"24", + "scope":"global" + }, + { + "family":"inet6", + "address":"fd42:bd00:7b11:2167:216:3eff:fe78:2ef3", + "netmask":"64", + "scope":"global" + }, + { + "family":"inet6", + "address":"fe80::216:3eff:fed3:7af3", + "netmask":"64", + "scope":"link" + } + ] + }, + "lo":{ + "addresses":[ + { + "family":"inet", + "address":"127.0.0.1", + "netmask":"8", + "scope":"local" + }, + { + "family":"inet6", + "address":"::1", + "netmask":"128", + "scope":"local" + } + ] + } + } + } + } + } + }, + + "networks":{ + "my-macvlan":{ + "state":{ + "metadata":{ + "addresses":[ + { + "family":"inet", + "address":"192.168.178.199", + "netmask":"24", + "scope":"global" + }, + { + "family":"inet6", + "address":"fd42:bd00:7b11:2167:216:3eff:fe78:2ef3", + "netmask":"64", + "scope":"global" + }, + { + "family":"inet6", + "address":"fe80::216:3eff:fed3:7af3", + "netmask":"64", + "scope":"link" + } + ], + "vlan":{ + "lower_device":"eno1", + "vid":666 + } + } + } + }, + "lo":{ + "state":{ + "metadata":{ + "addresses":[ + { + "family":"inet", + "address":"127.0.0.1", + "netmask":"8", + "scope":"local" + }, + { + "family":"inet6", + "address":"::1", + "netmask":"128", + "scope":"local" + } + ], + "vlan":null + } + } + }, + "eno1":{ + "state":{ + "metadata":{ + "addresses":[ + { + "family":"inet", + "address":"192.168.178.126", + "netmask":"24", + "scope":"global" + }, + { + "family":"inet6", + "address":"fe80::3c0b:7da9:3cc7:9e40", + "netmask":"64", + "scope":"link" + } + ], + "vlan":null + } + } + }, + "eno1.666":{ + "state":{ + "metadata":{ + "addresses":[ + { + "family":"inet6", + "address":"fe80::de4a:3eff:fe8d:f356", + "netmask":"64", + "scope":"link" + } + ], + "vlan":{ + "lower_device":"eno1", + "vid":666 + } + } + } + } + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd.license b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/lxd_inventory.atd.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json new file mode 100644 index 000000000..f7be74f90 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json @@ -0,0 +1,222 @@ +[ + { + "DEPLOY_ID": "bcfec9d9-c0d0-4523-b5e7-62993947e94c", + "ETIME": 0, + "GID": 105, + "GNAME": "SW", + "HISTORY_RECORDS": {}, + "ID": 451, + "LAST_POLL": 0, + "LCM_STATE": 3, + "MONITORING": {}, + "NAME": "terraform_demo_00", + "RESCHED": 0, + "STATE": 3, + "STIME": 1649886492, + "TEMPLATE": { + "NIC": [ + { + "AR_ID": "0", + "BRIDGE": "mgmt0", + "BRIDGE_TYPE": "linux", + "CLUSTER_ID": "0", + "IP": "192.168.11.248", + "MAC": "02:00:c0:a8:2b:bb", + "MODEL": "virtio", + "NAME": "NIC0", + "NETWORK": "Infrastructure", + "NETWORK_ID": "0", + "NIC_ID": "0", + "SECURITY_GROUPS": "0,101", + "TARGET": "one-453-0", + "VLAN_ID": "12", + "VN_MAD": "802.1Q" + } + ], + "NIC_DEFAULT": { + "MODEL": "virtio" + }, + "TEMPLATE_ID": "28", + "TM_MAD_SYSTEM": "shared", + "VCPU": "4", + "VMID": "453" + }, + "USER_TEMPLATE": { + "GUEST_OS": "linux", + "INPUTS_ORDER": "", + "LABELS": "foo,bench", + "LOGO": "images/logos/linux.png", + "MEMORY_UNIT_COST": "MB", + "SCHED_REQUIREMENTS": "ARCH=\"x86_64\"", + "TGROUP": "bench_clients" + } + }, + { + "DEPLOY_ID": "25895435-5e3a-4d50-a025-e03a7a463abd", + "ETIME": 0, + "GID": 105, + "GNAME": "SW", + "HISTORY_RECORDS": {}, + "ID": 451, + "LAST_POLL": 0, + "LCM_STATE": 3, + "MONITORING": {}, + "NAME": "terraform_demo_01", + "RESCHED": 0, + "STATE": 3, + "STIME": 1649886492, + "TEMPLATE": { + "NIC": [ + { + "AR_ID": "0", + "BRIDGE": "mgmt0", + "BRIDGE_TYPE": "linux", + "CLUSTER_ID": "0", + "IP": "192.168.11.241", + "MAC": "02:00:c0:a8:4b:bb", + "MODEL": "virtio", + "NAME": "NIC0", + "NETWORK": "Infrastructure", + "NETWORK_ID": "0", + "NIC_ID": "0", + "SECURITY_GROUPS": "0,101", + "TARGET": "one-451-0", + "VLAN_ID": "12", + "VN_MAD": "802.1Q" + } + ], + "NIC_DEFAULT": { + "MODEL": "virtio" + }, + "TEMPLATE_ID": "28", + "TM_MAD_SYSTEM": "shared", + "VCPU": "4", + "VMID": "451" + }, + "USER_TEMPLATE": { + "GUEST_OS": "linux", + "INPUTS_ORDER": "", + "LABELS": "foo,bench", + "LOGO": "images/logos/linux.png", + "MEMORY_UNIT_COST": "MB", + "SCHED_REQUIREMENTS": "ARCH=\"x86_64\"", + "TESTATTR": "testvar", + "TGROUP": "bench_clients" + } + }, + { + "DEPLOY_ID": "2b00c379-3601-45ee-acf5-e7b3ff2b7bca", + "ETIME": 0, + "GID": 105, + "GNAME": "SW", + "HISTORY_RECORDS": {}, + "ID": 451, + "LAST_POLL": 0, + "LCM_STATE": 3, + "MONITORING": {}, + "NAME": "terraform_demo_srv_00", + "RESCHED": 0, + "STATE": 3, + "STIME": 1649886492, + "TEMPLATE": { + "NIC": [ + { + "AR_ID": "0", + "BRIDGE": "mgmt0", + "BRIDGE_TYPE": "linux", + "CLUSTER_ID": "0", + "IP": "192.168.11.247", + "MAC": "02:00:c0:a8:0b:cc", + "MODEL": "virtio", + "NAME": "NIC0", + "NETWORK": "Infrastructure", + "NETWORK_ID": "0", + "NIC_ID": "0", + "SECURITY_GROUPS": "0,101", + "TARGET": "one-452-0", + "VLAN_ID": "12", + "VN_MAD": "802.1Q" + } + ], + "NIC_DEFAULT": { + "MODEL": "virtio" + }, + "TEMPLATE_ID": "28", + "TM_MAD_SYSTEM": "shared", + "VCPU": "4", + "VMID": "452" + }, + "USER_TEMPLATE": { + "GUEST_OS": "linux", + "INPUTS_ORDER": "", + "LABELS": "serv,bench", + "LOGO": "images/logos/linux.png", + "MEMORY_UNIT_COST": "MB", + "SCHED_REQUIREMENTS": "ARCH=\"x86_64\"", + "TGROUP": "bench_server" + } + }, + { + "DEPLOY_ID": "97037f55-dd2c-4549-8d24-561a6569e870", + "ETIME": 0, + "GID": 105, + "GNAME": "SW", + "HISTORY_RECORDS": {}, + "ID": 311, + "LAST_POLL": 0, + "LCM_STATE": 3, + "MONITORING": {}, + "NAME": "bs-windows", + "RESCHED": 0, + "STATE": 3, + "STIME": 1648076254, + "TEMPLATE": { + "NIC": [ + { + "AR_ID": "0", + "BRIDGE": "mgmt0", + "BRIDGE_TYPE": "linux", + "CLUSTER_ID": "0", + "IP": "192.168.11.209", + "MAC": "02:00:c0:a8:0b:dd", + "MODEL": "virtio", + "NAME": "NIC0", + "NETWORK": "Infrastructure", + "NETWORK_ID": "0", + "NETWORK_UNAME": "admin", + "NIC_ID": "0", + "SECURITY_GROUPS": "0,101", + "TARGET": "one-311-0", + "VLAN_ID": "12", + "VN_MAD": "802.1Q" + }, + [ + "TEMPLATE_ID", + "23" + ], + [ + "TM_MAD_SYSTEM", + "shared" + ], + [ + "VCPU", + "4" + ], + [ + "VMID", + "311" + ] + ] + }, + "UID": 22, + "UNAME": "bsanders", + "USER_TEMPLATE": { + "GUEST_OS": "windows", + "INPUTS_ORDER": "", + "LABELS": "serv", + "HYPERVISOR": "kvm", + "SCHED_REQUIREMENTS": "ARCH=\"x86_64\"", + "SET_HOSTNAME": "windows" + } + } +] diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json.license b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/fixtures/opennebula_inventory.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_cobbler.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_cobbler.py new file mode 100644 index 000000000..a09001ad6 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_cobbler.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Orion Poplawski +# 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 + +from ansible_collections.community.general.plugins.inventory.cobbler import InventoryModule + + +@pytest.fixture(scope="module") +def inventory(): + return InventoryModule() + + +def test_init_cache(inventory): + inventory._init_cache() + assert inventory._cache[inventory.cache_key] == {} + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "foobar.cobbler.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.cobbler.yml') is False 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 new file mode 100644 index 000000000..e3928b0db --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Cliff Hults +# 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 +# +# The API responses used in these tests were recorded from PVE version 6.2. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible.inventory.data import InventoryData +from ansible_collections.community.general.plugins.inventory.icinga2 import InventoryModule + + +@pytest.fixture(scope="module") +def inventory(): + r = InventoryModule() + r.inventory = InventoryData() + return r + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.icinga2.yml') is False + + +def check_api(): + return True + + +# NOTE: when updating/adding replies to this function, +# be sure to only add only the _contents_ of the 'data' dict in the API reply +def query_hosts(hosts=None, attrs=None, joins=None, host_filter=None): + # _get_hosts - list of dicts + json_host_data = [ + { + 'attrs': { + 'address': 'test-host1.home.local', + 'groups': ['home_servers', 'servers_dell'], + 'display_name': 'Test Host 1', + 'state': 0.0, + 'state_type': 1.0 + }, + 'joins': {}, + 'meta': {}, + 'name': 'test-host1', + 'type': 'Host' + }, + { + 'attrs': { + 'address': 'test-host2.home.local', + 'display_name': 'Test Host 2', + 'groups': ['home_servers', 'servers_hp'], + 'state': 1.0, + 'state_type': 1.0 + }, + 'joins': {}, + 'meta': {}, + 'name': 'test-host2', + 'type': 'Host' + }, + { + 'attrs': { + 'address': '', + 'display_name': 'Test Host 3', + 'groups': ['not_home_servers', 'servers_hp'], + 'state': 1.0, + 'state_type': 1.0 + }, + 'joins': {}, + 'meta': {}, + 'name': 'test-host3.example.com', + 'type': 'Host' + } + ] + return json_host_data + + +def get_option(option): + if option == 'groups': + return {} + elif option == 'keyed_groups': + return [] + elif option == 'compose': + return {} + elif option == 'strict': + return False + else: + return None + + +def test_populate(inventory, mocker): + # module settings + inventory.icinga2_user = 'ansible' + inventory.icinga2_password = 'password' + inventory.icinga2_url = 'https://localhost:5665' + '/v1' + inventory.inventory_attr = "address" + + # bypass authentication and API fetch calls + inventory._check_api = mocker.MagicMock(side_effect=check_api) + inventory._query_hosts = mocker.MagicMock(side_effect=query_hosts) + inventory.get_option = mocker.MagicMock(side_effect=get_option) + inventory._populate() + + # get different hosts + host1_info = inventory.inventory.get_host('test-host1.home.local') + print(host1_info) + host2_info = inventory.inventory.get_host('test-host2.home.local') + print(host2_info) + host3_info = inventory.inventory.get_host('test-host3.example.com') + assert inventory.inventory.get_host('test-host3.example.com') is not None + print(host3_info) + + # check if host in the home_servers group + assert 'home_servers' in inventory.inventory.groups + group1_data = inventory.inventory.groups['home_servers'] + group1_test_data = [host1_info, host2_info] + print(group1_data.hosts) + print(group1_test_data) + assert group1_data.hosts == group1_test_data + # Test servers_hp group + group2_data = inventory.inventory.groups['servers_hp'] + group2_test_data = [host2_info, host3_info] + print(group2_data.hosts) + print(group2_test_data) + assert group2_data.hosts == group2_test_data + + # check if host state rules apply properly + assert host1_info.get_vars()['state'] == 'on' + assert host1_info.get_vars()['display_name'] == "Test Host 1" + assert host2_info.get_vars()['state'] == 'off' + assert host3_info.get_vars().get('ansible_host') is None + + # Confirm attribute options switcher + inventory.inventory_attr = "name" + inventory._populate() + assert inventory.inventory.get_host('test-host3.example.com') is not None + host2_info = inventory.inventory.get_host('test-host2') + assert host2_info is not None + assert host2_info.get_vars().get('ansible_host') == 'test-host2.home.local' + + # Confirm attribute options switcher + inventory.inventory_attr = "display_name" + inventory._populate() + assert inventory.inventory.get_host('Test Host 3') is not None + host2_info = inventory.inventory.get_host('Test Host 2') + assert host2_info is not None + assert host2_info.get_vars().get('ansible_host') == 'test-host2.home.local' 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 new file mode 100644 index 000000000..a4f556761 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Copyright 2018 Luke Murphy +# 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 sys + +linode_apiv4 = pytest.importorskip('linode_api4') +mandatory_py_version = pytest.mark.skipif( + sys.version_info < (2, 7), + reason='The linode_api4 dependency requires python2.7 or higher' +) + + +from ansible.errors import AnsibleError +from ansible.parsing.dataloader import DataLoader +from ansible.template import Templar +from ansible_collections.community.general.plugins.inventory.linode import InventoryModule + + +@pytest.fixture(scope="module") +def inventory(): + plugin = InventoryModule() + plugin.templar = Templar(loader=DataLoader()) + return plugin + + +def test_missing_access_token_lookup(inventory): + loader = DataLoader() + inventory._options = {'access_token': None} + with pytest.raises(AnsibleError) as error_message: + inventory._build_client(loader) + assert 'Could not retrieve Linode access token' in error_message + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "foobar.linode.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.linode.yml') is False diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_lxd.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_lxd.py new file mode 100644 index 000000000..a1f31fdc5 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_lxd.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frank Dornheim +# 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 + +from ansible.inventory.data import InventoryData +from ansible_collections.community.general.plugins.inventory.lxd import InventoryModule + + +HOST_COMPARATIVE_DATA = { + 'ansible_connection': 'ssh', 'ansible_host': '10.98.143.199', 'ansible_lxd_os': 'ubuntu', 'ansible_lxd_release': 'focal', + 'ansible_lxd_profile': ['default'], 'ansible_lxd_state': 'running', 'ansible_lxd_location': 'Berlin', + 'ansible_lxd_vlan_ids': {'my-macvlan': 666}, 'inventory_hostname': 'vlantest', 'inventory_hostname_short': 'vlantest'} +GROUP_COMPARATIVE_DATA = { + 'all': [], 'ungrouped': [], 'testpattern': ['vlantest'], 'vlan666': ['vlantest'], 'locationBerlin': ['vlantest'], + 'osUbuntu': ['vlantest'], 'releaseFocal': ['vlantest'], 'releaseBionic': [], 'profileDefault': ['vlantest'], + 'profileX11': [], 'netRangeIPv4': ['vlantest'], 'netRangeIPv6': ['vlantest']} +GROUP_Config = { + 'testpattern': {'type': 'pattern', 'attribute': 'test'}, + 'vlan666': {'type': 'vlanid', 'attribute': 666}, + 'locationBerlin': {'type': 'location', 'attribute': 'Berlin'}, + 'osUbuntu': {'type': 'os', 'attribute': 'ubuntu'}, + 'releaseFocal': {'type': 'release', 'attribute': 'focal'}, + 'releaseBionic': {'type': 'release', 'attribute': 'bionic'}, + 'profileDefault': {'type': 'profile', 'attribute': 'default'}, + 'profileX11': {'type': 'profile', 'attribute': 'x11'}, + 'netRangeIPv4': {'type': 'network_range', 'attribute': '10.98.143.0/24'}, + 'netRangeIPv6': {'type': 'network_range', 'attribute': 'fd42:bd00:7b11:2167:216:3eff::/96'}} + + +@pytest.fixture +def inventory(): + inv = InventoryModule() + inv.inventory = InventoryData() + + # Test Values + inv.data = inv.load_json_data('tests/unit/plugins/inventory/fixtures/lxd_inventory.atd') # Load Test Data + inv.groupby = GROUP_Config + inv.prefered_instance_network_interface = 'eth' + inv.prefered_instance_network_family = 'inet' + inv.filter = 'running' + inv.dump_data = False + inv.type_filter = 'both' + + return inv + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "foobar.lxd.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.lxd.yml') is False + + +def test_build_inventory_hosts(inventory): + """Load example data and start the inventoryto test the host generation. + + After the inventory plugin has run with the test data, the result of the host is checked.""" + inventory._populate() + generated_data = inventory.inventory.get_host('vlantest').get_vars() + + eq = True + for key, value in HOST_COMPARATIVE_DATA.items(): + if generated_data[key] != value: + eq = False + assert eq + + +def test_build_inventory_groups(inventory): + """Load example data and start the inventory to test the group generation. + + After the inventory plugin has run with the test data, the result of the host is checked.""" + inventory._populate() + generated_data = inventory.inventory.get_groups_dict() + + eq = True + for key, value in GROUP_COMPARATIVE_DATA.items(): + if generated_data[key] != value: + eq = False + assert eq + + +def test_build_inventory_groups_with_no_groupselection(inventory): + """Load example data and start the inventory to test the group generation with groupby is none. + + After the inventory plugin has run with the test data, the result of the host is checked.""" + inventory.groupby = None + inventory._populate() + generated_data = inventory.inventory.get_groups_dict() + group_comparative_data = {'all': [], 'ungrouped': []} + + eq = True + print("data: {0}".format(generated_data)) + for key, value in group_comparative_data.items(): + if generated_data[key] != value: + eq = False + assert eq diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_opennebula.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_opennebula.py new file mode 100644 index 000000000..bbc2fe699 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_opennebula.py @@ -0,0 +1,342 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, FELDSAM s.r.o. - FeldHost™ +# 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 +# +# The API responses used in these tests were recorded from OpenNebula version 5.10. + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +from collections import OrderedDict +import json + +import pytest + +from ansible.inventory.data import InventoryData +from ansible.parsing.dataloader import DataLoader +from ansible.template import Templar +from ansible_collections.community.general.plugins.inventory.opennebula import InventoryModule +from ansible_collections.community.general.tests.unit.compat.mock import create_autospec + + +@pytest.fixture +def inventory(): + r = InventoryModule() + r.inventory = InventoryData() + return r + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "foobar.opennebula.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.opennebula.yml') is False + + +def get_vm_pool_json(): + with open('tests/unit/plugins/inventory/fixtures/opennebula_inventory.json', 'r') as json_file: + jsondata = json.load(json_file) + + data = type('pyone.bindings.VM_POOLSub', (object,), {'VM': []})() + + for fake_server in jsondata: + data.VM.append(type('pyone.bindings.VMType90Sub', (object,), fake_server)()) + + return data + + +def get_vm_pool(): + data = type('pyone.bindings.VM_POOLSub', (object,), {'VM': []})() + + vm = type('pyone.bindings.VMType90Sub', (object,), { + 'DEPLOY_ID': 'one-7157', + 'ETIME': 0, + 'GID': 132, + 'GNAME': 'CSApparelVDC', + 'HISTORY_RECORDS': {}, + 'ID': 7157, + 'LAST_POLL': 1632762935, + 'LCM_STATE': 3, + 'MONITORING': {}, + 'NAME': 'sam-691-sam', + 'RESCHED': 0, + 'SNAPSHOTS': [], + 'STATE': 3, + 'STIME': 1632755245, + 'TEMPLATE': OrderedDict({ + 'NIC': OrderedDict({ + 'AR_ID': '0', + 'BRIDGE': 'onebr80', + 'BRIDGE_TYPE': 'linux', + 'CLUSTER_ID': '0', + 'IP': '172.22.4.187', + 'MAC': '02:00:ac:16:04:bb', + 'MTU': '8192', + 'NAME': 'NIC0', + 'NETWORK': 'Private Net CSApparel', + 'NETWORK_ID': '80', + 'NETWORK_UNAME': 'CSApparelVDC-admin', + 'NIC_ID': '0', + 'PHYDEV': 'team0', + 'SECURITY_GROUPS': '0', + 'TARGET': 'one-7157-0', + 'VLAN_ID': '480', + 'VN_MAD': '802.1Q' + }) + }), + 'USER_TEMPLATE': OrderedDict({ + 'HYPERVISOR': 'kvm', + 'INPUTS_ORDER': '', + 'LOGO': 'images/logos/centos.png', + 'MEMORY_UNIT_COST': 'MB', + 'SCHED_REQUIREMENTS': 'CLUSTER_ID="0"' + }) + })() + data.VM.append(vm) + + vm = type('pyone.bindings.VMType90Sub', (object,), { + 'DEPLOY_ID': 'one-327', + 'ETIME': 0, + 'GID': 0, + 'GNAME': 'oneadmin', + 'HISTORY_RECORDS': {}, + 'ID': 327, + 'LAST_POLL': 1632763543, + 'LCM_STATE': 3, + 'MONITORING': {}, + 'NAME': 'zabbix-327', + 'RESCHED': 0, + 'SNAPSHOTS': [], + 'STATE': 3, + 'STIME': 1575410106, + 'TEMPLATE': OrderedDict({ + 'NIC': [ + OrderedDict({ + 'AR_ID': '0', + 'BRIDGE': 'onerb.103', + 'BRIDGE_TYPE': 'linux', + 'IP': '185.165.1.1', + 'IP6_GLOBAL': '2000:a001::b9ff:feae:aa0d', + 'IP6_LINK': 'fe80::b9ff:feae:aa0d', + 'MAC': '02:00:b9:ae:aa:0d', + 'NAME': 'NIC0', + 'NETWORK': 'Public', + 'NETWORK_ID': '7', + 'NIC_ID': '0', + 'PHYDEV': 'team0', + 'SECURITY_GROUPS': '0', + 'TARGET': 'one-327-0', + 'VLAN_ID': '100', + 'VN_MAD': '802.1Q' + }), + OrderedDict({ + 'AR_ID': '0', + 'BRIDGE': 'br0', + 'BRIDGE_TYPE': 'linux', + 'CLUSTER_ID': '0', + 'IP': '192.168.1.1', + 'MAC': '02:00:c0:a8:3b:01', + 'NAME': 'NIC1', + 'NETWORK': 'Management', + 'NETWORK_ID': '11', + 'NIC_ID': '1', + 'SECURITY_GROUPS': '0', + 'TARGET': 'one-327-1', + 'VN_MAD': 'bridge' + }) + ] + }), + 'USER_TEMPLATE': OrderedDict({ + 'HYPERVISOR': 'kvm', + 'INPUTS_ORDER': '', + 'LABELS': 'Oracle Linux', + 'LOGO': 'images/logos/centos.png', + 'MEMORY_UNIT_COST': 'MB', + 'SAVED_TEMPLATE_ID': '29' + }) + })() + data.VM.append(vm) + + vm = type('pyone.bindings.VMType90Sub', (object,), { + 'DEPLOY_ID': 'one-107', + 'ETIME': 0, + 'GID': 0, + 'GNAME': 'oneadmin', + 'HISTORY_RECORDS': {}, + 'ID': 107, + 'LAST_POLL': 1632764186, + 'LCM_STATE': 3, + 'MONITORING': {}, + 'NAME': 'gitlab-107', + 'RESCHED': 0, + 'SNAPSHOTS': [], + 'STATE': 3, + 'STIME': 1572485522, + 'TEMPLATE': OrderedDict({ + 'NIC': OrderedDict({ + 'AR_ID': '0', + 'BRIDGE': 'onerb.103', + 'BRIDGE_TYPE': 'linux', + 'IP': '185.165.1.3', + 'IP6_GLOBAL': '2000:a001::b9ff:feae:aa03', + 'IP6_LINK': 'fe80::b9ff:feae:aa03', + 'MAC': '02:00:b9:ae:aa:03', + 'NAME': 'NIC0', + 'NETWORK': 'Public', + 'NETWORK_ID': '7', + 'NIC_ID': '0', + 'PHYDEV': 'team0', + 'SECURITY_GROUPS': '0', + 'TARGET': 'one-107-0', + 'VLAN_ID': '100', + 'VN_MAD': '802.1Q' + }) + }), + 'USER_TEMPLATE': OrderedDict({ + 'HYPERVISOR': 'kvm', + 'INPUTS_ORDER': '', + 'LABELS': 'Gitlab,Centos', + 'LOGO': 'images/logos/centos.png', + 'MEMORY_UNIT_COST': 'MB', + 'SCHED_REQUIREMENTS': 'ID="0" | ID="1" | ID="2"', + 'SSH_PORT': '8822' + }) + })() + data.VM.append(vm) + + return data + + +options_base_test = { + 'api_url': 'https://opennebula:2633/RPC2', + 'api_username': 'username', + 'api_password': 'password', + 'api_authfile': '~/.one/one_auth', + 'hostname': 'v4_first_ip', + 'group_by_labels': True, + 'filter_by_label': None, +} + +options_constructable_test = options_base_test.copy() +options_constructable_test.update({ + 'compose': {'is_linux': "GUEST_OS == 'linux'"}, + 'filter_by_label': 'bench', + 'groups': { + 'benchmark_clients': "TGROUP.endswith('clients')", + 'lin': 'is_linux == True' + }, + 'keyed_groups': [{'key': 'TGROUP', 'prefix': 'tgroup'}], + +}) + + +# given a dictionary `opts_dict`, return a function that behaves like ansible's inventory get_options +def mk_get_options(opts_dict): + def inner(opt): + return opts_dict.get(opt, False) + return inner + + +def test_get_connection_info(inventory, mocker): + inventory.get_option = mocker.MagicMock(side_effect=mk_get_options(options_base_test)) + + auth = inventory._get_connection_info() + assert (auth.username and auth.password) + + +def test_populate_constructable_templating(inventory, mocker): + # bypass API fetch call + inventory._get_vm_pool = mocker.MagicMock(side_effect=get_vm_pool_json) + inventory.get_option = mocker.MagicMock(side_effect=mk_get_options(options_constructable_test)) + + # the templating engine is needed for the constructable groups/vars + # so give that some fake data and instantiate it. + fake_config_filepath = '/fake/opennebula.yml' + fake_cache = {fake_config_filepath: options_constructable_test.copy()} + fake_cache[fake_config_filepath]['plugin'] = 'community.general.opennebula' + dataloader = create_autospec(DataLoader, instance=True) + dataloader._FILE_CACHE = fake_cache + inventory.templar = Templar(loader=dataloader) + + inventory._populate() + + # note the vm_pool (and json data file) has four hosts, + # but options_constructable_test asks ansible to filter it out + assert len(get_vm_pool_json().VM) == 4 + assert set([vm.NAME for vm in get_vm_pool_json().VM]) == set([ + 'terraform_demo_00', + 'terraform_demo_01', + 'terraform_demo_srv_00', + 'bs-windows', + ]) + assert set(inventory.inventory.hosts) == set(['terraform_demo_00', 'terraform_demo_01', 'terraform_demo_srv_00']) + + host_demo00 = inventory.inventory.get_host('terraform_demo_00') + host_demo01 = inventory.inventory.get_host('terraform_demo_01') + host_demosrv = inventory.inventory.get_host('terraform_demo_srv_00') + + assert 'benchmark_clients' in inventory.inventory.groups + assert 'lin' in inventory.inventory.groups + assert inventory.inventory.groups['benchmark_clients'].hosts == [host_demo00, host_demo01] + assert inventory.inventory.groups['lin'].hosts == [host_demo00, host_demo01, host_demosrv] + + # test group by label: + assert 'bench' in inventory.inventory.groups + assert 'foo' in inventory.inventory.groups + assert inventory.inventory.groups['bench'].hosts == [host_demo00, host_demo01, host_demosrv] + assert inventory.inventory.groups['serv'].hosts == [host_demosrv] + assert inventory.inventory.groups['foo'].hosts == [host_demo00, host_demo01] + + # test `compose` transforms GUEST_OS=Linux to is_linux == True + assert host_demo00.get_vars()['GUEST_OS'] == 'linux' + assert host_demo00.get_vars()['is_linux'] is True + + # test `keyed_groups` + assert inventory.inventory.groups['tgroup_bench_clients'].hosts == [host_demo00, host_demo01] + assert inventory.inventory.groups['tgroup_bench_server'].hosts == [host_demosrv] + + +def test_populate(inventory, mocker): + # bypass API fetch call + inventory._get_vm_pool = mocker.MagicMock(side_effect=get_vm_pool) + inventory.get_option = mocker.MagicMock(side_effect=mk_get_options(options_base_test)) + inventory._populate() + + # get different hosts + host_sam = inventory.inventory.get_host('sam-691-sam') + host_zabbix = inventory.inventory.get_host('zabbix-327') + host_gitlab = inventory.inventory.get_host('gitlab-107') + + # test if groups exists + assert 'Gitlab' in inventory.inventory.groups + assert 'Centos' in inventory.inventory.groups + assert 'Oracle_Linux' in inventory.inventory.groups + + # check if host_zabbix is in Oracle_Linux group + group_oracle_linux = inventory.inventory.groups['Oracle_Linux'] + assert group_oracle_linux.hosts == [host_zabbix] + + # check if host_gitlab is in Gitlab and Centos group + group_gitlab = inventory.inventory.groups['Gitlab'] + group_centos = inventory.inventory.groups['Centos'] + assert group_gitlab.hosts == [host_gitlab] + assert group_centos.hosts == [host_gitlab] + + # check IPv4 address + assert '172.22.4.187' == host_sam.get_vars()['v4_first_ip'] + + # check IPv6 address + assert '2000:a001::b9ff:feae:aa0d' == host_zabbix.get_vars()['v6_first_ip'] + + # check ansible_hosts + assert '172.22.4.187' == host_sam.get_vars()['ansible_host'] + assert '185.165.1.1' == host_zabbix.get_vars()['ansible_host'] + assert '185.165.1.3' == host_gitlab.get_vars()['ansible_host'] + + # check for custom ssh port + assert '8822' == host_gitlab.get_vars()['ansible_port'] 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 new file mode 100644 index 000000000..13832c938 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py @@ -0,0 +1,745 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Jeffrey van Pelt +# 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 +# +# The API responses used in these tests were recorded from PVE version 6.2. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible.inventory.data import InventoryData +from ansible_collections.community.general.plugins.inventory.proxmox import InventoryModule + + +@pytest.fixture(scope="module") +def inventory(): + r = InventoryModule() + r.inventory = InventoryData() + return r + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "foobar.proxmox.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.proxmox.yml') is False + + +def get_auth(): + return True + + +# NOTE: when updating/adding replies to this function, +# be sure to only add only the _contents_ of the 'data' dict in the API reply +def get_json(url): + if url == "https://localhost:8006/api2/json/nodes": + # _get_nodes + return [{"type": "node", + "cpu": 0.01, + "maxdisk": 500, + "mem": 500, + "node": "testnode", + "id": "node/testnode", + "maxcpu": 1, + "status": "online", + "ssl_fingerprint": "xx", + "disk": 1000, + "maxmem": 1000, + "uptime": 10000, + "level": ""}, + {"type": "node", + "node": "testnode2", + "id": "node/testnode2", + "status": "offline", + "ssl_fingerprint": "yy"}] + elif url == "https://localhost:8006/api2/json/pools": + # _get_pools + return [{"poolid": "test"}] + elif url == "https://localhost:8006/api2/json/nodes/testnode/lxc": + # _get_lxc_per_node + return [{"cpus": 1, + "name": "test-lxc", + "cpu": 0.01, + "diskwrite": 0, + "lock": "", + "maxmem": 1000, + "template": "", + "diskread": 0, + "mem": 1000, + "swap": 0, + "type": "lxc", + "maxswap": 0, + "maxdisk": "1000", + "netout": 1000, + "pid": "1000", + "netin": 1000, + "status": "running", + "vmid": "100", + "disk": "1000", + "uptime": 1000}] + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu": + # _get_qemu_per_node + return [{"name": "test-qemu", + "cpus": 1, + "mem": 1000, + "template": "", + "diskread": 0, + "cpu": 0.01, + "maxmem": 1000, + "diskwrite": 0, + "netout": 1000, + "pid": "1001", + "netin": 1000, + "maxdisk": 1000, + "vmid": "101", + "uptime": 1000, + "disk": 0, + "status": "running"}, + {"name": "test-qemu-windows", + "cpus": 1, + "mem": 1000, + "template": "", + "diskread": 0, + "cpu": 0.01, + "maxmem": 1000, + "diskwrite": 0, + "netout": 1000, + "pid": "1001", + "netin": 1000, + "maxdisk": 1000, + "vmid": "102", + "uptime": 1000, + "disk": 0, + "status": "running"}, + {"name": "test-qemu-multi-nic", + "cpus": 1, + "mem": 1000, + "template": "", + "diskread": 0, + "cpu": 0.01, + "maxmem": 1000, + "diskwrite": 0, + "netout": 1000, + "pid": "1001", + "netin": 1000, + "maxdisk": 1000, + "vmid": "103", + "uptime": 1000, + "disk": 0, + "status": "running"}, + {"name": "test-qemu-template", + "cpus": 1, + "mem": 0, + "template": 1, + "diskread": 0, + "cpu": 0, + "maxmem": 1000, + "diskwrite": 0, + "netout": 0, + "pid": "1001", + "netin": 0, + "maxdisk": 1000, + "vmid": "9001", + "uptime": 0, + "disk": 0, + "status": "stopped"}] + elif url == "https://localhost:8006/api2/json/pools/test": + # _get_members_per_pool + return {"members": [{"uptime": 1000, + "template": 0, + "id": "qemu/101", + "mem": 1000, + "status": "running", + "cpu": 0.01, + "maxmem": 1000, + "diskwrite": 1000, + "name": "test-qemu", + "netout": 1000, + "netin": 1000, + "vmid": 101, + "node": "testnode", + "maxcpu": 1, + "type": "qemu", + "maxdisk": 1000, + "disk": 0, + "diskread": 1000}]} + elif url == "https://localhost:8006/api2/json/nodes/testnode/network": + # _get_node_ip + return [{"families": ["inet"], + "priority": 3, + "active": 1, + "cidr": "10.1.1.2/24", + "iface": "eth0", + "method": "static", + "exists": 1, + "type": "eth", + "netmask": "24", + "gateway": "10.1.1.1", + "address": "10.1.1.2", + "method6": "manual", + "autostart": 1}, + {"method6": "manual", + "autostart": 1, + "type": "OVSPort", + "exists": 1, + "method": "manual", + "iface": "eth1", + "ovs_bridge": "vmbr0", + "active": 1, + "families": ["inet"], + "priority": 5, + "ovs_type": "OVSPort"}, + {"type": "OVSBridge", + "method": "manual", + "iface": "vmbr0", + "families": ["inet"], + "priority": 4, + "ovs_ports": "eth1", + "ovs_type": "OVSBridge", + "method6": "manual", + "autostart": 1, + "active": 1}] + elif url == "https://localhost:8006/api2/json/nodes/testnode/lxc/100/config": + # _get_vm_config (lxc) + return { + "console": 1, + "rootfs": "local-lvm:vm-100-disk-0,size=4G", + "cmode": "tty", + "description": "A testnode", + "cores": 1, + "hostname": "test-lxc", + "arch": "amd64", + "tty": 2, + "swap": 0, + "cpulimit": "0", + "net0": "name=eth0,bridge=vmbr0,gw=10.1.1.1,hwaddr=FF:FF:FF:FF:FF:FF,ip=10.1.1.3/24,type=veth", + "ostype": "ubuntu", + "digest": "123456789abcdef0123456789abcdef01234567890", + "protection": 0, + "memory": 1000, + "onboot": 0, + "cpuunits": 1024, + "tags": "one, two, three", + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/101/config": + # _get_vm_config (qemu) + return { + "tags": "one, two, three", + "cores": 1, + "ide2": "none,media=cdrom", + "memory": 1000, + "kvm": 1, + "digest": "0123456789abcdef0123456789abcdef0123456789", + "description": "A test qemu", + "sockets": 1, + "onboot": 1, + "vmgenid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "numa": 0, + "bootdisk": "scsi0", + "cpu": "host", + "name": "test-qemu", + "ostype": "l26", + "hotplug": "network,disk,usb", + "scsi0": "local-lvm:vm-101-disk-0,size=8G", + "net0": "virtio=ff:ff:ff:ff:ff:ff,bridge=vmbr0,firewall=1", + "agent": "1,fstrim_cloned_disks=1", + "bios": "seabios", + "ide0": "local-lvm:vm-101-cloudinit,media=cdrom,size=4M", + "boot": "cdn", + "scsihw": "virtio-scsi-pci", + "smbios1": "uuid=ffffffff-ffff-ffff-ffff-ffffffffffff" + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/102/config": + # _get_vm_config (qemu) + return { + "numa": 0, + "digest": "460add1531a7068d2ae62d54f67e8fb9493dece9", + "ide2": "none,media=cdrom", + "bootdisk": "sata0", + "name": "test-qemu-windows", + "balloon": 0, + "cpulimit": "4", + "agent": "1", + "cores": 6, + "sata0": "storage:vm-102-disk-0,size=100G", + "memory": 10240, + "smbios1": "uuid=127301fc-0122-48d5-8fc5-c04fa78d8146", + "scsihw": "virtio-scsi-pci", + "sockets": 1, + "ostype": "win8", + "net0": "virtio=ff:ff:ff:ff:ff:ff,bridge=vmbr0", + "onboot": 1 + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/103/config": + # _get_vm_config (qemu) + return { + 'scsi1': 'storage:vm-103-disk-3,size=30G', + 'sockets': 1, + 'memory': 8192, + 'ostype': 'l26', + 'scsihw': 'virtio-scsi-pci', + "net0": "virtio=ff:ff:ff:ff:ff:ff,bridge=vmbr0", + "net1": "virtio=ff:ff:ff:ff:ff:ff,bridge=vmbr1", + 'bootdisk': 'scsi0', + 'scsi0': 'storage:vm-103-disk-0,size=10G', + 'name': 'test-qemu-multi-nic', + 'cores': 4, + 'digest': '51b7599f869b9a3f564804a0aed290f3de803292', + 'smbios1': 'uuid=863b31c3-42ca-4a92-aed7-4111f342f70a', + 'agent': '1,type=virtio', + 'ide2': 'none,media=cdrom', + 'balloon': 0, + 'numa': 0, + 'scsi2': 'storage:vm-103-disk-2,size=10G', + 'serial0': 'socket', + 'vmgenid': 'ddfb79b2-b484-4d66-88e7-6e76f2d1be77', + 'onboot': 1, + 'tablet': 0 + } + + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/101/agent/network-get-interfaces": + # _get_agent_network_interfaces + return {"result": [ + { + "hardware-address": "00:00:00:00:00:00", + "ip-addresses": [ + { + "prefix": 8, + "ip-address-type": "ipv4", + "ip-address": "127.0.0.1" + }, + { + "ip-address-type": "ipv6", + "ip-address": "::1", + "prefix": 128 + }], + "statistics": { + "rx-errs": 0, + "rx-bytes": 163244, + "rx-packets": 1623, + "rx-dropped": 0, + "tx-dropped": 0, + "tx-packets": 1623, + "tx-bytes": 163244, + "tx-errs": 0}, + "name": "lo"}, + { + "statistics": { + "rx-packets": 4025, + "rx-dropped": 12, + "rx-bytes": 324105, + "rx-errs": 0, + "tx-errs": 0, + "tx-bytes": 368860, + "tx-packets": 3479, + "tx-dropped": 0}, + "name": "eth0", + "ip-addresses": [ + { + "prefix": 24, + "ip-address-type": "ipv4", + "ip-address": "10.1.2.3" + }, + { + "prefix": 64, + "ip-address": "fd8c:4687:e88d:1be3:5b70:7b88:c79c:293", + "ip-address-type": "ipv6" + }], + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "hardware-address": "ff:ff:ff:ff:ff:ff", + "ip-addresses": [ + { + "prefix": 16, + "ip-address": "10.10.2.3", + "ip-address-type": "ipv4" + }], + "name": "docker0", + "statistics": { + "rx-bytes": 0, + "rx-errs": 0, + "rx-dropped": 0, + "rx-packets": 0, + "tx-packets": 0, + "tx-dropped": 0, + "tx-errs": 0, + "tx-bytes": 0 + }}]} + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/102/agent/network-get-interfaces": + # _get_agent_network_interfaces + return {"result": {'error': {'desc': 'this feature or command is not currently supported', 'class': 'Unsupported'}}} + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/103/agent/network-get-interfaces": + # _get_agent_network_interfaces + return { + "result": [ + { + "statistics": { + "tx-errs": 0, + "rx-errs": 0, + "rx-dropped": 0, + "tx-bytes": 48132932372, + "tx-dropped": 0, + "rx-bytes": 48132932372, + "tx-packets": 178578980, + "rx-packets": 178578980 + }, + "hardware-address": "ff:ff:ff:ff:ff:ff", + "ip-addresses": [ + { + "ip-address-type": "ipv4", + "prefix": 8, + "ip-address": "127.0.0.1" + } + ], + "name": "lo" + }, + { + "name": "eth0", + "ip-addresses": [ + { + "ip-address-type": "ipv4", + "prefix": 24, + "ip-address": "172.16.0.143" + } + ], + "statistics": { + "rx-errs": 0, + "tx-errs": 0, + "rx-packets": 660028, + "tx-packets": 304599, + "tx-dropped": 0, + "rx-bytes": 1846743499, + "tx-bytes": 1287844926, + "rx-dropped": 0 + }, + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "name": "eth1", + "hardware-address": "ff:ff:ff:ff:ff:ff", + "statistics": { + "rx-bytes": 235717091946, + "tx-dropped": 0, + "rx-dropped": 0, + "tx-bytes": 123411636251, + "rx-packets": 540431277, + "tx-packets": 468411864, + "rx-errs": 0, + "tx-errs": 0 + }, + "ip-addresses": [ + { + "ip-address": "10.0.0.133", + "prefix": 24, + "ip-address-type": "ipv4" + } + ] + }, + { + "name": "docker0", + "ip-addresses": [ + { + "ip-address": "172.17.0.1", + "prefix": 16, + "ip-address-type": "ipv4" + } + ], + "hardware-address": "ff:ff:ff:ff:ff:ff", + "statistics": { + "rx-errs": 0, + "tx-errs": 0, + "rx-packets": 0, + "tx-packets": 0, + "tx-dropped": 0, + "rx-bytes": 0, + "rx-dropped": 0, + "tx-bytes": 0 + } + }, + { + "hardware-address": "ff:ff:ff:ff:ff:ff", + "name": "datapath" + }, + { + "name": "weave", + "ip-addresses": [ + { + "ip-address": "10.42.0.1", + "ip-address-type": "ipv4", + "prefix": 16 + } + ], + "hardware-address": "ff:ff:ff:ff:ff:ff", + "statistics": { + "rx-bytes": 127289123306, + "tx-dropped": 0, + "rx-dropped": 0, + "tx-bytes": 43827573343, + "rx-packets": 132750542, + "tx-packets": 74218762, + "rx-errs": 0, + "tx-errs": 0 + } + }, + { + "name": "vethwe-datapath", + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "name": "vethwe-bridge", + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "hardware-address": "ff:ff:ff:ff:ff:ff", + "name": "vxlan-6784" + }, + { + "name": "vethwepl0dfe1fe", + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "name": "vethweplf1e7715", + "hardware-address": "ff:ff:ff:ff:ff:ff" + }, + { + "hardware-address": "ff:ff:ff:ff:ff:ff", + "name": "vethwepl9d244a1" + }, + { + "hardware-address": "ff:ff:ff:ff:ff:ff", + "name": "vethwepl2ca477b" + }, + { + "name": "nomacorip", + } + ] + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/lxc/100/status/current": + # _get_vm_status (lxc) + return { + "swap": 0, + "name": "test-lxc", + "diskread": 0, + "vmid": 100, + "diskwrite": 0, + "pid": 9000, + "mem": 89980928, + "netin": 1950776396424, + "disk": 4998168576, + "cpu": 0.00163430613110039, + "type": "lxc", + "uptime": 6793736, + "maxmem": 1073741824, + "status": "running", + "cpus": "1", + "ha": { + "group": 'null', + "state": "started", + "managed": 1 + }, + "maxdisk": 3348329267200, + "netout": 1947793356037, + "maxswap": 1073741824 + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/101/status/current": + # _get_vm_status (qemu) + return { + "status": "stopped", + "uptime": 0, + "maxmem": 5364514816, + "maxdisk": 34359738368, + "netout": 0, + "cpus": 2, + "ha": { + "managed": 0 + }, + "diskread": 0, + "vmid": 101, + "diskwrite": 0, + "name": "test-qemu", + "cpu": 0, + "disk": 0, + "netin": 0, + "mem": 0, + "qmpstatus": "stopped" + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/102/status/current": + # _get_vm_status (qemu) + return { + "status": "stopped", + "uptime": 0, + "maxmem": 5364514816, + "maxdisk": 34359738368, + "netout": 0, + "cpus": 2, + "ha": { + "managed": 0 + }, + "diskread": 0, + "vmid": 102, + "diskwrite": 0, + "name": "test-qemu-windows", + "cpu": 0, + "disk": 0, + "netin": 0, + "mem": 0, + "qmpstatus": "prelaunch" + } + elif url == "https://localhost:8006/api2/json/nodes/testnode/qemu/103/status/current": + # _get_vm_status (qemu) + return { + "status": "stopped", + "uptime": 0, + "maxmem": 5364514816, + "maxdisk": 34359738368, + "netout": 0, + "cpus": 2, + "ha": { + "managed": 0 + }, + "diskread": 0, + "vmid": 103, + "diskwrite": 0, + "name": "test-qemu-multi-nic", + "cpu": 0, + "disk": 0, + "netin": 0, + "mem": 0, + "qmpstatus": "paused" + } + + +def get_vm_snapshots(node, properties, vmtype, vmid, name): + return [ + {"description": "", + "name": "clean", + "snaptime": 1000, + "vmstate": 0 + }, + {"name": "current", + "digest": "1234689abcdf", + "running": 0, + "description": "You are here!", + "parent": "clean" + }] + + +def get_option(opts): + def fn(option): + default = opts.get('default', False) + return opts.get(option, default) + return fn + + +def test_populate(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 + + opts = { + 'group_prefix': 'proxmox_', + 'facts_prefix': 'proxmox_', + 'want_facts': True, + 'want_proxmox_nodes_ansible_host': True, + 'qemu_extended_statuses': 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() + + # get different hosts + host_qemu = inventory.inventory.get_host('test-qemu') + host_qemu_windows = inventory.inventory.get_host('test-qemu-windows') + host_qemu_multi_nic = inventory.inventory.get_host('test-qemu-multi-nic') + host_qemu_template = inventory.inventory.get_host('test-qemu-template') + host_lxc = inventory.inventory.get_host('test-lxc') + + # check if qemu-test is in the proxmox_pool_test group + assert 'proxmox_pool_test' in inventory.inventory.groups + group_qemu = inventory.inventory.groups['proxmox_pool_test'] + assert group_qemu.hosts == [host_qemu] + + # check if qemu-test has eth0 interface in agent_interfaces fact + assert 'eth0' in [d['name'] for d in host_qemu.get_vars()['proxmox_agent_interfaces']] + + # check if qemu-multi-nic has multiple network interfaces + for iface_name in ['eth0', 'eth1', 'weave']: + assert iface_name in [d['name'] for d in host_qemu_multi_nic.get_vars()['proxmox_agent_interfaces']] + + # check if interface with no mac-address or ip-address defaults correctly + assert [iface for iface in host_qemu_multi_nic.get_vars()['proxmox_agent_interfaces'] + if iface['name'] == 'nomacorip' + and iface['mac-address'] == '' + and iface['ip-addresses'] == [] + ] + + # check to make sure qemu-windows doesn't have proxmox_agent_interfaces + assert "proxmox_agent_interfaces" not in host_qemu_windows.get_vars() + + # check if lxc-test has been discovered correctly + group_lxc = inventory.inventory.groups['proxmox_all_lxc'] + assert group_lxc.hosts == [host_lxc] + + # check if qemu template is not present + assert host_qemu_template is None + + # check that offline node is in inventory + assert inventory.inventory.get_host('testnode2') + + # make sure that ['prelaunch', 'paused'] are in the group list + for group in ['paused', 'prelaunch']: + assert ('%sall_%s' % (inventory.group_prefix, group)) in inventory.inventory.groups + + # check if qemu-windows is in the prelaunch group + group_prelaunch = inventory.inventory.groups['proxmox_all_prelaunch'] + assert group_prelaunch.hosts == [host_qemu_windows] + + # check if qemu-multi-nic is in the paused group + group_paused = inventory.inventory.groups['proxmox_all_paused'] + assert group_paused.hosts == [host_qemu_multi_nic] + + +def test_populate_missing_qemu_extended_groups(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 + + opts = { + 'group_prefix': 'proxmox_', + 'facts_prefix': 'proxmox_', + 'want_facts': True, + 'want_proxmox_nodes_ansible_host': True, + 'qemu_extended_statuses': False + } + + # 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 ['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 diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_stackpath_compute.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_stackpath_compute.py new file mode 100644 index 000000000..781db50b7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_stackpath_compute.py @@ -0,0 +1,206 @@ +# Copyright (c) 2020 Shay Rybak +# Copyright (c) 2020 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 pytest + +from ansible.errors import AnsibleError +from ansible.inventory.data import InventoryData +from ansible_collections.community.general.plugins.inventory.stackpath_compute import InventoryModule + + +@pytest.fixture(scope="module") +def inventory(): + r = InventoryModule() + r.inventory = InventoryData() + return r + + +def test_get_stack_slugs(inventory): + stacks = [ + { + 'status': 'ACTIVE', + 'name': 'test1', + 'id': 'XXXX', + 'updatedAt': '2020-07-08T01:00:00.000000Z', + 'slug': 'test1', + 'createdAt': '2020-07-08T00:00:00.000000Z', + 'accountId': 'XXXX', + }, { + 'status': 'ACTIVE', + 'name': 'test2', + 'id': 'XXXX', + 'updatedAt': '2019-10-22T18:00:00.000000Z', + 'slug': 'test2', + 'createdAt': '2019-10-22T18:00:00.000000Z', + 'accountId': 'XXXX', + }, { + 'status': 'DISABLED', + 'name': 'test3', + 'id': 'XXXX', + 'updatedAt': '2020-01-16T20:00:00.000000Z', + 'slug': 'test3', + 'createdAt': '2019-10-15T13:00:00.000000Z', + 'accountId': 'XXXX', + }, { + 'status': 'ACTIVE', + 'name': 'test4', + 'id': 'XXXX', + 'updatedAt': '2019-11-20T22:00:00.000000Z', + 'slug': 'test4', + 'createdAt': '2019-11-20T22:00:00.000000Z', + 'accountId': 'XXXX', + } + ] + inventory._get_stack_slugs(stacks) + assert len(inventory.stack_slugs) == 4 + assert inventory.stack_slugs == [ + "test1", + "test2", + "test3", + "test4" + ] + + +def test_verify_file(tmp_path, inventory): + file = tmp_path / "foobar.stackpath_compute.yml" + file.touch() + assert inventory.verify_file(str(file)) is True + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.stackpath_compute.yml') is False + + +def test_validate_config(inventory): + config = { + "client_secret": "short_client_secret", + "use_internal_ip": False, + "stack_slugs": ["test1"], + "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "plugin": "community.general.stackpath_compute", + } + with pytest.raises(AnsibleError) as error_message: + inventory._validate_config(config) + assert "client_secret must be 64 characters long" in error_message + + config = { + "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "use_internal_ip": True, + "stack_slugs": ["test1"], + "client_id": "short_client_id", + "plugin": "community.general.stackpath_compute", + } + with pytest.raises(AnsibleError) as error_message: + inventory._validate_config(config) + assert "client_id must be 32 characters long" in error_message + + config = { + "use_internal_ip": True, + "stack_slugs": ["test1"], + "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "plugin": "community.general.stackpath_compute", + } + with pytest.raises(AnsibleError) as error_message: + inventory._validate_config(config) + assert "config missing client_secret, a required parameter" in error_message + + config = { + "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "use_internal_ip": False, + "plugin": "community.general.stackpath_compute", + } + with pytest.raises(AnsibleError) as error_message: + inventory._validate_config(config) + assert "config missing client_id, a required parameter" in error_message + + +def test_populate(inventory): + instances = [ + { + "name": "instance1", + "countryCode": "SE", + "workloadSlug": "wokrload1", + "continent": "Europe", + "workloadId": "id1", + "cityCode": "ARN", + "externalIpAddress": "20.0.0.1", + "target": "target1", + "stackSlug": "stack1", + "ipAddress": "10.0.0.1", + }, + { + "name": "instance2", + "countryCode": "US", + "workloadSlug": "wokrload2", + "continent": "America", + "workloadId": "id2", + "cityCode": "JFK", + "externalIpAddress": "20.0.0.2", + "target": "target2", + "stackSlug": "stack1", + "ipAddress": "10.0.0.2", + }, + { + "name": "instance3", + "countryCode": "SE", + "workloadSlug": "workload3", + "continent": "Europe", + "workloadId": "id3", + "cityCode": "ARN", + "externalIpAddress": "20.0.0.3", + "target": "target1", + "stackSlug": "stack2", + "ipAddress": "10.0.0.3", + }, + { + "name": "instance4", + "countryCode": "US", + "workloadSlug": "workload3", + "continent": "America", + "workloadId": "id4", + "cityCode": "JFK", + "externalIpAddress": "20.0.0.4", + "target": "target2", + "stackSlug": "stack2", + "ipAddress": "10.0.0.4", + }, + ] + inventory.hostname_key = "externalIpAddress" + inventory._populate(instances) + # get different hosts + host1 = inventory.inventory.get_host('20.0.0.1') + host2 = inventory.inventory.get_host('20.0.0.2') + host3 = inventory.inventory.get_host('20.0.0.3') + host4 = inventory.inventory.get_host('20.0.0.4') + + # get different groups + assert 'citycode_arn' in inventory.inventory.groups + group_citycode_arn = inventory.inventory.groups['citycode_arn'] + assert 'countrycode_se' in inventory.inventory.groups + group_countrycode_se = inventory.inventory.groups['countrycode_se'] + assert 'continent_america' in inventory.inventory.groups + group_continent_america = inventory.inventory.groups['continent_america'] + assert 'name_instance1' in inventory.inventory.groups + group_name_instance1 = inventory.inventory.groups['name_instance1'] + assert 'stackslug_stack1' in inventory.inventory.groups + group_stackslug_stack1 = inventory.inventory.groups['stackslug_stack1'] + assert 'target_target1' in inventory.inventory.groups + group_target_target1 = inventory.inventory.groups['target_target1'] + assert 'workloadslug_workload3' in inventory.inventory.groups + group_workloadslug_workload3 = inventory.inventory.groups['workloadslug_workload3'] + assert 'workloadid_id1' in inventory.inventory.groups + group_workloadid_id1 = inventory.inventory.groups['workloadid_id1'] + + assert group_citycode_arn.hosts == [host1, host3] + assert group_countrycode_se.hosts == [host1, host3] + assert group_continent_america.hosts == [host2, host4] + assert group_name_instance1.hosts == [host1] + assert group_stackslug_stack1.hosts == [host1, host2] + assert group_target_target1.hosts == [host1, host3] + assert group_workloadslug_workload3.hosts == [host3, host4] + assert group_workloadid_id1.hosts == [host1] diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_xen_orchestra.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_xen_orchestra.py new file mode 100644 index 000000000..bae038e80 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_xen_orchestra.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Jeffrey van Pelt +# 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 +# +# The API responses used in these tests were recorded from PVE version 6.2. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible.inventory.data import InventoryData +from ansible_collections.community.general.plugins.inventory.xen_orchestra import InventoryModule + +objects = { + 'vms': { + '0e64588-2bea-2d82-e922-881654b0a48f': + { + 'type': 'VM', + 'addresses': {}, + 'CPUs': {'max': 4, 'number': 4}, + 'memory': {'dynamic': [1073741824, 2147483648], 'static': [536870912, 4294967296], 'size': 2147483648}, + 'name_description': '', + 'name_label': 'XCP-NG lab 2', + 'os_version': {}, + 'parent': 'd3af89b2-d846-0874-6acb-031ccf11c560', + 'power_state': 'Running', + 'tags': [], + 'id': '0e645898-2bea-2d82-e922-881654b0a48f', + 'uuid': '0e645898-2bea-2d82-e922-881654b0a48f', + '$pool': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$poolId': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$container': '222d8594-9426-468a-ad69-7a6f02330fa3' + }, + 'b0d25e70-019d-6182-2f7c-b0f5d8ef9331': + { + 'type': 'VM', + 'addresses': {'0/ipv4/0': '192.168.1.55', '1/ipv4/0': '10.0.90.1'}, + 'CPUs': {'max': 4, 'number': 4}, + 'mainIpAddress': '192.168.1.55', + 'memory': {'dynamic': [2147483648, 2147483648], 'static': [134217728, 2147483648], 'size': 2147483648}, + 'name_description': '', + 'name_label': 'XCP-NG lab 3', + 'os_version': {'name': 'FreeBSD 11.3-STABLE', 'uname': '11.3-STABLE', 'distro': 'FreeBSD'}, + 'power_state': 'Halted', + 'tags': [], + 'id': 'b0d25e70-019d-6182-2f7c-b0f5d8ef9331', + 'uuid': 'b0d25e70-019d-6182-2f7c-b0f5d8ef9331', + '$pool': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$poolId': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$container': 'c96ec4dd-28ac-4df4-b73c-4371bd202728', + } + }, + 'pools': { + '3d315997-73bd-5a74-8ca7-289206cb03ab': { + 'master': '222d8594-9426-468a-ad69-7a6f02330fa3', + 'tags': [], + 'name_description': '', + 'name_label': 'Storage Lab', + 'cpus': {'cores': 120, 'sockets': 6}, + 'id': '3d315997-73bd-5a74-8ca7-289206cb03ab', + 'type': 'pool', + 'uuid': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$pool': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$poolId': '3d315997-73bd-5a74-8ca7-289206cb03ab' + } + }, + 'hosts': { + 'c96ec4dd-28ac-4df4-b73c-4371bd202728': { + 'type': 'host', + 'uuid': 'c96ec4dd-28ac-4df4-b73c-4371bd202728', + 'enabled': True, + 'CPUs': { + 'cpu_count': '40', + 'socket_count': '2', + 'vendor': 'GenuineIntel', + 'speed': '1699.998', + 'modelname': 'Intel(R) Xeon(R) CPU E5-2650L v2 @ 1.70GHz', + 'family': '6', + 'model': '62', + 'stepping': '4' + }, + 'address': '172.16.210.14', + 'build': 'release/stockholm/master/7', + 'cpus': {'cores': 40, 'sockets': 2}, + 'hostname': 'r620-s1', + 'name_description': 'Default install', + 'name_label': 'R620-S1', + 'memory': {'usage': 45283590144, 'size': 137391292416}, + 'power_state': 'Running', + 'tags': [], + 'version': '8.2.0', + 'productBrand': 'XCP-ng', + 'id': 'c96ec4dd-28ac-4df4-b73c-4371bd202728', + '$pool': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$poolId': '3d315997-73bd-5a74-8ca7-289206cb03ab' + }, + '222d8594-9426-468a-ad69-7a6f02330fa3': { + 'type': 'host', + 'uuid': '222d8594-9426-468a-ad69-7a6f02330fa3', + 'enabled': True, + 'CPUs': { + 'cpu_count': '40', + 'socket_count': '2', + 'vendor': 'GenuineIntel', + 'speed': '1700.007', + 'modelname': 'Intel(R) Xeon(R) CPU E5-2650L v2 @ 1.70GHz', + 'family': '6', + 'model': '62', + 'stepping': '4' + }, + 'address': '172.16.210.16', + 'build': 'release/stockholm/master/7', + 'cpus': {'cores': 40, 'sockets': 2}, + 'hostname': 'r620-s2', + 'name_description': 'Default install', + 'name_label': 'R620-S2', + 'memory': {'usage': 10636521472, 'size': 137391292416}, + 'power_state': 'Running', + 'tags': ['foo', 'bar', 'baz'], + 'version': '8.2.0', + 'productBrand': 'XCP-ng', + 'id': '222d8594-9426-468a-ad69-7a6f02330fa3', + '$pool': '3d315997-73bd-5a74-8ca7-289206cb03ab', + '$poolId': '3d315997-73bd-5a74-8ca7-289206cb03ab' + } + } +} + + +def get_option(option): + if option == 'groups': + return {} + elif option == 'keyed_groups': + return [] + elif option == 'compose': + return {} + elif option == 'strict': + return False + else: + return None + + +def serialize_groups(groups): + return list(map(str, groups)) + + +@ pytest.fixture(scope="module") +def inventory(): + r = InventoryModule() + r.inventory = InventoryData() + return r + + +def test_verify_file_bad_config(inventory): + assert inventory.verify_file('foobar.xen_orchestra.yml') is False + + +def test_populate(inventory, mocker): + inventory.get_option = mocker.MagicMock(side_effect=get_option) + inventory._populate(objects) + actual = sorted(inventory.inventory.hosts.keys()) + expected = sorted(['c96ec4dd-28ac-4df4-b73c-4371bd202728', '222d8594-9426-468a-ad69-7a6f02330fa3', + '0e64588-2bea-2d82-e922-881654b0a48f', 'b0d25e70-019d-6182-2f7c-b0f5d8ef9331']) + + assert actual == expected + + # Host with ip assertions + host_with_ip = inventory.inventory.get_host( + 'b0d25e70-019d-6182-2f7c-b0f5d8ef9331') + host_with_ip_vars = host_with_ip.vars + + assert host_with_ip_vars['ansible_host'] == '192.168.1.55' + assert host_with_ip_vars['power_state'] == 'halted' + assert host_with_ip_vars['type'] == 'VM' + + assert host_with_ip in inventory.inventory.groups['with_ip'].hosts + + # Host without ip + host_without_ip = inventory.inventory.get_host( + '0e64588-2bea-2d82-e922-881654b0a48f') + host_without_ip_vars = host_without_ip.vars + + assert host_without_ip_vars['ansible_host'] is None + assert host_without_ip_vars['power_state'] == 'running' + + assert host_without_ip in inventory.inventory.groups['without_ip'].hosts + + assert host_with_ip in inventory.inventory.groups['xo_host_r620_s1'].hosts + assert host_without_ip in inventory.inventory.groups['xo_host_r620_s2'].hosts + + r620_s1 = inventory.inventory.get_host( + 'c96ec4dd-28ac-4df4-b73c-4371bd202728') + r620_s2 = inventory.inventory.get_host( + '222d8594-9426-468a-ad69-7a6f02330fa3') + + assert r620_s1.vars['address'] == '172.16.210.14' + assert r620_s1.vars['tags'] == [] + assert r620_s2.vars['address'] == '172.16.210.16' + assert r620_s2.vars['tags'] == ['foo', 'bar', 'baz'] + + storage_lab = inventory.inventory.groups['xo_pool_storage_lab'] + + # Check that hosts are in their corresponding pool + assert r620_s1 in storage_lab.hosts + assert r620_s2 in storage_lab.hosts + + # Check that hosts are in their corresponding pool + assert host_without_ip in storage_lab.hosts + assert host_with_ip in storage_lab.hosts 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 new file mode 100644 index 000000000..092979225 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py @@ -0,0 +1,85 @@ +# Copyright (c) 2022 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 os +import json + +from ansible_collections.community.general.plugins.lookup.onepassword import ( + OnePassCLIv1, + OnePassCLIv2, +) + + +def load_file(file): + with open((os.path.join(os.path.dirname(__file__), "onepassword_fixtures", file)), "r") as f: + return json.loads(f.read()) + + +# Intentionally excludes metadata leaf nodes that would exist in real output if not relevant. +MOCK_ENTRIES = { + OnePassCLIv1: [ + { + 'vault_name': 'Acme "Quot\'d" Servers', + 'queries': [ + '0123456789', + 'Mock "Quot\'d" Server' + ], + 'expected': ['t0pS3cret', 't0pS3cret'], + 'output': load_file("v1_out_01.json"), + }, + { + 'vault_name': 'Acme Logins', + 'queries': [ + '9876543210', + 'Mock Website', + 'acme.com' + ], + 'expected': ['t0pS3cret', 't0pS3cret', 't0pS3cret'], + 'output': load_file("v1_out_02.json"), + }, + { + 'vault_name': 'Acme Logins', + 'queries': [ + '864201357' + ], + 'expected': ['vauxhall'], + 'output': load_file("v1_out_03.json"), + }, + ], + OnePassCLIv2: [ + { + "vault_name": "Test Vault", + "queries": [ + "ywvdbojsguzgrgnokmcxtydgdv", + "Authy Backup", + ], + "expected": ["OctoberPoppyNuttyDraperySabbath", "OctoberPoppyNuttyDraperySabbath"], + "output": load_file("v2_out_01.json"), + }, + { + # Request a custom field where ID and label are different + "vault_name": "Test Vault", + "queries": ["Dummy Login"], + "kwargs": { + "field": "password1", + }, + "expected": ["data in custom field"], + "output": load_file("v2_out_02.json") + }, + { + # Request data from a custom section + "vault_name": "Test Vault", + "queries": ["Duplicate Sections"], + "kwargs": { + "field": "s2 text", + "section": "Section 2", + }, + "expected": ["first value"], + "output": load_file("v2_out_03.json") + }, + ], +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py new file mode 100644 index 000000000..18afae1a3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py @@ -0,0 +1,39 @@ +# Copyright (c) 2020 Ansible Project +# 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 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._config._config_file_path = "/home/jin/.op/config" + mocker.patch.object(op._cli, "_run") + + return op + + return _fake_op + + +@pytest.fixture +def opv1(fake_op): + return fake_op("1.17.2") + + +@pytest.fixture +def opv2(fake_op): + return fake_op("2.27.2") diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json new file mode 100644 index 000000000..57eab09c5 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json @@ -0,0 +1,18 @@ +{ + "uuid": "0123456789", + "vaultUuid": "2468", + "overview": { + "title": "Mock \"Quot'd\" Server" + }, + "details": { + "sections": [{ + "title": "", + "fields": [ + {"t": "username", "v": "jamesbond"}, + {"t": "password", "v": "t0pS3cret"}, + {"t": "notes", "v": "Test note with\nmultiple lines and trailing space.\n\n"}, + {"t": "tricksy \"quot'd\" field\\", "v": "\"quot'd\" value"} + ] + }] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.json.license new file mode 100644 index 000000000..969b956c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_01.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/v1_out_02.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.json new file mode 100644 index 000000000..da133fe59 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.json @@ -0,0 +1,18 @@ +{ + "uuid": "9876543210", + "vaultUuid": "1357", + "overview": { + "title": "Mock Website", + "URLs": [ + {"l": "website", "u": "https://acme.com/login"} + ] + }, + "details": { + "sections": [{ + "title": "", + "fields": [ + {"t": "password", "v": "t0pS3cret"} + ] + }] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.json.license new file mode 100644 index 000000000..969b956c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_02.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/v1_out_03.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.json new file mode 100644 index 000000000..57c7d0f3d --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.json @@ -0,0 +1,20 @@ +{ + "uuid": "864201357", + "vaultUuid": "1357", + "overview": { + "title": "Mock Something" + }, + "details": { + "fields": [ + { + "value": "jbond@mi6.gov.uk", + "name": "emailAddress" + }, + { + "name": "password", + "value": "vauxhall" + }, + {} + ] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.json.license new file mode 100644 index 000000000..969b956c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v1_out_03.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_01.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json new file mode 100644 index 000000000..7ef0bb0c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json @@ -0,0 +1,35 @@ +{ + "id": "ywvdbojsguzgrgnokmcxtydgdv", + "title": "Authy Backup", + "version": 1, + "vault": { + "id": "bcqxysvcnejjrwzoqrwzcqjqxc", + "name": "test vault" + }, + "category": "PASSWORD", + "last_edited_by": "7FUPZ8ZNE02KSHMAIMKHIVUE17", + "created_at": "2015-01-18T13:13:38Z", + "updated_at": "2016-02-20T16:23:54Z", + "additional_information": "Jan 18, 2015, 08:13:38", + "fields": [ + { + "id": "password", + "type": "CONCEALED", + "purpose": "PASSWORD", + "label": "password", + "value": "OctoberPoppyNuttyDraperySabbath", + "reference": "op://Test Vault/Authy Backup/password", + "password_details": { + "strength": "FANTASTIC" + } + }, + { + "id": "notesPlain", + "type": "STRING", + "purpose": "NOTES", + "label": "notesPlain", + "value": "Backup password to restore Authy", + "reference": "op://Test Vault/Authy Backup/notesPlain" + } + ] +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json.license new file mode 100644 index 000000000..969b956c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.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_02.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.json new file mode 100644 index 000000000..5da2a16d1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.json @@ -0,0 +1,85 @@ +{ + "id": "awk4s2u44fhnrgppszcsvc663i", + "title": "Dummy Login", + "version": 4, + "vault": { + "id": "stpebbaccrq72xulgouxsk4p7y", + "name": "Personal" + }, + "category": "LOGIN", + "last_edited_by": "LSGPJERUYBH7BFPHMZ2KKGL6AU", + "created_at": "2018-04-25T21:55:19Z", + "updated_at": "2022-09-02T17:51:21Z", + "additional_information": "agent.smith", + "urls": [ + { + "primary": true, + "href": "https://acme.com" + } + ], + "sections": [ + { + "id": "add more" + }, + { + "id": "gafaeg7vnqmgrklw5r6yrufyxy", + "label": "COMMANDS" + }, + { + "id": "linked items", + "label": "Related Items" + } + ], + "fields": [ + { + "id": "username", + "type": "STRING", + "purpose": "USERNAME", + "label": "username", + "value": "agent.smith", + "reference": "op://Personal/Dummy Login/username" + }, + { + "id": "password", + "type": "CONCEALED", + "purpose": "PASSWORD", + "label": "password", + "value": "FootworkDegreeReverence", + "entropy": 159.60836791992188, + "reference": "op://Personal/Dummy Login/password", + "password_details": { + "entropy": 159, + "generated": true, + "strength": "FANTASTIC" + } + }, + { + "id": "notesPlain", + "type": "STRING", + "purpose": "NOTES", + "label": "notesPlain", + "reference": "op://Personal/Dummy Login/notesPlain" + }, + { + "id": "7gyjekelk24ghgd4rvafspjbli", + "section": { + "id": "add more" + }, + "type": "STRING", + "label": "title", + "value": "value of the field", + "reference": "op://Personal/Dummy Login/add more/title" + }, + { + "id": "fx4wpzokrxn7tlb3uwpdjfptgm", + "section": { + "id": "gafaeg7vnqmgrklw5r6yrufyxy", + "label": "COMMANDS" + }, + "type": "CONCEALED", + "label": "password1", + "value": "data in custom field", + "reference": "op://Personal/Dummy Login/COMMANDS/password1" + } + ] +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.json.license new file mode 100644 index 000000000..969b956c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_02.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_03.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.json new file mode 100644 index 000000000..22fbc3f29 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.json @@ -0,0 +1,103 @@ +{ + "id": "7t7qu2r35qyvqj3crujd4dqxmy", + "title": "Duplicate Sections", + "version": 3, + "vault": { + "id": "stpebbaccrq72xulgouxsk4p7y", + "name": "Personal" + }, + "category": "LOGIN", + "last_edited_by": "LSGPJERUYBH7BFPHMZ2KKGL6AU", + "created_at": "2022-11-04T17:09:18Z", + "updated_at": "2022-11-04T17:22:19Z", + "additional_information": "flora", + "urls": [ + { + "label": "website", + "primary": true, + "href": "https://acme.com/login" + } + ], + "sections": [ + { + "id": "add more" + }, + { + "id": "7osqcvd43i75teocdzbb6d7mie", + "label": "Section 2" + } + ], + "fields": [ + { + "id": "username", + "type": "STRING", + "purpose": "USERNAME", + "label": "username", + "value": "flora", + "reference": "op://Personal/Duplicate Sections/username" + }, + { + "id": "password", + "type": "CONCEALED", + "purpose": "PASSWORD", + "label": "password", + "value": "PtZGFLAibx-erTo7ywywEvh-n4syas97n-tuF2D.b8DdqA2vCjrvRGkNQxj!Gi9R", + "entropy": 379.564697265625, + "reference": "op://Personal/Duplicate Sections/password", + "password_details": { + "entropy": 379, + "generated": true, + "strength": "FANTASTIC" + } + }, + { + "id": "notesPlain", + "type": "STRING", + "purpose": "NOTES", + "label": "notesPlain", + "reference": "op://Personal/Duplicate Sections/notesPlain" + }, + { + "id": "4saaazkb7arwisj6ysctb4jmm4", + "section": { + "id": "add more" + }, + "type": "STRING", + "label": "text", + "value": "text field the first", + "reference": "op://Personal/Duplicate Sections/add more/text" + }, + { + "id": "4vtfkj4bwcmg7d5uf62wnpkp3a", + "section": { + "id": "add more" + }, + "type": "STRING", + "label": "text", + "value": "text field the second", + "reference": "op://Personal/Duplicate Sections/add more/text" + }, + { + "id": "wbrjnowkrgavpooomtht36gjqu", + "section": { + "id": "7osqcvd43i75teocdzbb6d7mie", + "label": "Section 2" + }, + "type": "STRING", + "label": "s2 text", + "value": "first value", + "reference": "op://Personal/Duplicate Sections/Section 2/s2 text" + }, + { + "id": "bddlz2fj2pebmtfhksbmcexy7m", + "section": { + "id": "7osqcvd43i75teocdzbb6d7mie", + "label": "Section 2" + }, + "type": "STRING", + "label": "s2 text", + "value": "second value", + "reference": "op://Personal/Duplicate Sections/Section 2/s2 text" + } + ] +} diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.json.license new file mode 100644 index 000000000..969b956c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_03.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 new file mode 100644 index 000000000..d45263965 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Jonathan Lung +# 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.errors import AnsibleError +from ansible.module_utils import six +from ansible.plugins.loader import lookup_loader +from ansible_collections.community.general.plugins.lookup.bitwarden import Bitwarden + + +MOCK_RECORDS = [ + { + "collectionIds": [], + "deletedDate": None, + "favorite": False, + "fields": [ + { + "linkedId": None, + "name": "a_new_secret", + "type": 1, + "value": "this is a new secret" + }, + { + "linkedId": None, + "name": "not so secret", + "type": 0, + "value": "not secret" + } + ], + "folderId": "3b12a9da-7c49-40b8-ad33-aede017a7ead", + "id": "90992f63-ddb6-4e76-8bfc-aede016ca5eb", + "login": { + "password": "passwordA3", + "passwordRevisionDate": "2022-07-26T23:03:23.399Z", + "totp": None, + "username": "userA" + }, + "name": "a_test", + "notes": None, + "object": "item", + "organizationId": None, + "passwordHistory": [ + { + "lastUsedDate": "2022-07-26T23:03:23.405Z", + "password": "a_new_secret: this is secret" + }, + { + "lastUsedDate": "2022-07-26T23:03:23.399Z", + "password": "passwordA2" + }, + { + "lastUsedDate": "2022-07-26T22:59:52.885Z", + "password": "passwordA" + } + ], + "reprompt": 0, + "revisionDate": "2022-07-26T23:03:23.743Z", + "type": 1 + }, + { + "collectionIds": [], + "deletedDate": None, + "favorite": False, + "folderId": None, + "id": "5ebd4d31-104c-49fc-a09c-aedf003d28ad", + "login": { + "password": "b", + "passwordRevisionDate": None, + "totp": None, + "username": "a" + }, + "name": "dupe_name", + "notes": None, + "object": "item", + "organizationId": None, + "reprompt": 0, + "revisionDate": "2022-07-27T03:42:40.353Z", + "type": 1 + }, + { + "collectionIds": [], + "deletedDate": None, + "favorite": False, + "folderId": None, + "id": "90657653-6695-496d-9431-aedf003d3015", + "login": { + "password": "d", + "passwordRevisionDate": None, + "totp": None, + "username": "c" + }, + "name": "dupe_name", + "notes": None, + "object": "item", + "organizationId": None, + "reprompt": 0, + "revisionDate": "2022-07-27T03:42:46.673Z", + "type": 1 + } +] + + +class MockBitwarden(Bitwarden): + + unlocked = True + + def _get_matches(self, search_value, search_field="name", collection_id=None): + return list(filter(lambda record: record[search_field] == search_value, MOCK_RECORDS)) + + +class LoggedOutMockBitwarden(MockBitwarden): + + unlocked = False + + +class TestLookupModule(unittest.TestCase): + + def setUp(self): + self.lookup = lookup_loader.get('community.general.bitwarden') + + @patch('ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden', new=MockBitwarden()) + def test_bitwarden_plugin_no_match(self): + # Entry 0, "a_test" of the test input should have no duplicates. + self.assertEqual([], self.lookup.run(['not_here'], field='password')[0]) + + @patch('ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden', new=MockBitwarden()) + def test_bitwarden_plugin_fields(self): + # Entry 0, "a_test" of the test input should have no duplicates. + record = MOCK_RECORDS[0] + record_name = record['name'] + for k, v in six.iteritems(record['login']): + self.assertEqual([v], + self.lookup.run([record_name], field=k)[0]) + + @patch('ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden', new=MockBitwarden()) + def test_bitwarden_plugin_duplicates(self): + # There are two records with name dupe_name; we need to be order-insensitive with + # checking what was retrieved. + self.assertEqual(set(['b', 'd']), + set(self.lookup.run(['dupe_name'], field='password')[0])) + + @patch('ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden', new=MockBitwarden()) + def test_bitwarden_plugin_full_item(self): + # Try to retrieve the full record of the first entry where the name is "a_name". + self.assertEqual([MOCK_RECORDS[0]], + self.lookup.run(['a_test'])[0]) + + @patch('ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden', LoggedOutMockBitwarden()) + def test_bitwarden_plugin_unlocked(self): + record = MOCK_RECORDS[0] + record_name = record['name'] + with self.assertRaises(AnsibleError): + self.lookup.run([record_name], field='password') diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_dependent.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_dependent.py new file mode 100644 index 000000000..74d7c4123 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_dependent.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020-2021, Felix Fontein +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +from ansible_collections.community.internal_test_tools.tests.unit.compat.unittest import TestCase +from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import ( + MagicMock, +) + +from ansible.plugins.loader import lookup_loader + + +class TestLookupModule(TestCase): + def setUp(self): + templar = MagicMock() + templar._loader = None + self.lookup = lookup_loader.get("community.general.dependent", templar=templar) + + def test_empty(self): + self.assertListEqual(self.lookup.run([], None), []) + + def test_simple(self): + self.assertListEqual( + self.lookup.run( + [ + {'a': '[1, 2]'}, + {'b': '[item.a + 3, item.a + 6]'}, + {'c': '[item.a + item.b * 10]'}, + ], + {}, + ), + [ + {'a': 1, 'b': 4, 'c': 41}, + {'a': 1, 'b': 7, 'c': 71}, + {'a': 2, 'b': 5, 'c': 52}, + {'a': 2, 'b': 8, 'c': 82}, + ], + ) diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_dsv.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_dsv.py new file mode 100644 index 000000000..a9a2d30ee --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_dsv.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Adam Migus +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.community.general.tests.unit.compat.unittest import TestCase +from ansible_collections.community.general.tests.unit.compat.mock import ( + patch, + MagicMock, +) +from ansible_collections.community.general.plugins.lookup import dsv +from ansible.plugins.loader import lookup_loader + + +class MockSecretsVault(MagicMock): + RESPONSE = '{"foo": "bar"}' + + def get_secret_json(self, path): + return self.RESPONSE + + +class TestLookupModule(TestCase): + def setUp(self): + dsv.sdk_is_missing = False + self.lookup = lookup_loader.get("community.general.dsv") + + @patch( + "ansible_collections.community.general.plugins.lookup.dsv.LookupModule.Client", + MockSecretsVault(), + ) + def test_get_secret_json(self): + self.assertListEqual( + [MockSecretsVault.RESPONSE], + self.lookup.run( + ["/dummy"], + [], + **{"tenant": "dummy", "client_id": "dummy", "client_secret": "dummy", } + ), + ) diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_etcd3.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_etcd3.py new file mode 100644 index 000000000..e9ac777eb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_etcd3.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2020, SCC France, Eric Belhomme +# 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, MagicMock +from ansible_collections.community.general.plugins.lookup import etcd3 +from ansible.plugins.loader import lookup_loader + + +class FakeKVMetadata: + + def __init__(self, keyvalue, header): + self.key = keyvalue + self.create_revision = '' + self.mod_revision = '' + self.version = '' + self.lease_id = '' + self.response_header = header + + +class FakeEtcd3Client(MagicMock): + + def get_prefix(self, key): + for i in range(1, 4): + yield self.get('{0}_{1}'.format(key, i)) + + def get(self, key): + return ("{0} value".format(key), FakeKVMetadata(key, None)) + + +class TestLookupModule(unittest.TestCase): + + def setUp(self): + etcd3.HAS_ETCD = True + self.lookup = lookup_loader.get('community.general.etcd3') + + @patch('ansible_collections.community.general.plugins.lookup.etcd3.etcd3_client', FakeEtcd3Client()) + def test_key(self): + expected_result = [{'key': 'a_key', 'value': 'a_key value'}] + self.assertListEqual(expected_result, self.lookup.run(['a_key'], [])) + + @patch('ansible_collections.community.general.plugins.lookup.etcd3.etcd3_client', FakeEtcd3Client()) + def test_key_prefix(self): + expected_result = [ + {'key': 'a_key_1', 'value': 'a_key_1 value'}, + {'key': 'a_key_2', 'value': 'a_key_2 value'}, + {'key': 'a_key_3', 'value': 'a_key_3 value'}, + ] + self.assertListEqual(expected_result, self.lookup.run(['a_key'], [], **{'prefix': True})) diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_lastpass.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_lastpass.py new file mode 100644 index 000000000..5f65c9f63 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_lastpass.py @@ -0,0 +1,175 @@ +# Copyright (c) 2016 Andrew Zenk +# 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 + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from argparse import ArgumentParser + +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 AnsibleError +from ansible.module_utils import six +from ansible.plugins.loader import lookup_loader +from ansible_collections.community.general.plugins.lookup.lastpass import LPass, LPassException + + +MOCK_ENTRIES = [{'username': 'user', + 'name': 'Mock Entry', + 'password': 't0pS3cret passphrase entry!', + 'url': 'https://localhost/login', + 'notes': 'Test\nnote with multiple lines.\n', + 'id': '0123456789'}] + + +class MockLPass(LPass): + + _mock_logged_out = False + _mock_disconnected = False + + def _lookup_mock_entry(self, key): + for entry in MOCK_ENTRIES: + if key == entry['id'] or key == entry['name']: + return entry + + def _run(self, args, stdin=None, expected_rc=0): + # Mock behavior of lpass executable + base_options = ArgumentParser(add_help=False) + base_options.add_argument('--color', default="auto", choices=['auto', 'always', 'never']) + + p = ArgumentParser() + sp = p.add_subparsers(help='command', dest='subparser_name') + + logout_p = sp.add_parser('logout', parents=[base_options], help='logout') + show_p = sp.add_parser('show', parents=[base_options], help='show entry details') + + field_group = show_p.add_mutually_exclusive_group(required=True) + for field in MOCK_ENTRIES[0].keys(): + field_group.add_argument("--{0}".format(field), default=False, action='store_true') + field_group.add_argument('--field', default=None) + show_p.add_argument('selector', help='Unique Name or ID') + + args = p.parse_args(args) + + def mock_exit(output='', error='', rc=0): + if rc != expected_rc: + raise LPassException(error) + return output, error + + if args.color != 'never': + return mock_exit(error='Error: Mock only supports --color=never', rc=1) + + if args.subparser_name == 'logout': + if self._mock_logged_out: + return mock_exit(error='Error: Not currently logged in', rc=1) + + logged_in_error = 'Are you sure you would like to log out? [Y/n]' + if stdin and stdin.lower() == 'n\n': + return mock_exit(output='Log out: aborted.', error=logged_in_error, rc=1) + elif stdin and stdin.lower() == 'y\n': + return mock_exit(output='Log out: complete.', error=logged_in_error, rc=0) + else: + return mock_exit(error='Error: aborted response', rc=1) + + if args.subparser_name == 'show': + if self._mock_logged_out: + return mock_exit(error='Error: Could not find decryption key.' + + ' Perhaps you need to login with `lpass login`.', rc=1) + + if self._mock_disconnected: + return mock_exit(error='Error: Couldn\'t resolve host name.', rc=1) + + mock_entry = self._lookup_mock_entry(args.selector) + + if args.field: + return mock_exit(output=mock_entry.get(args.field, '')) + elif args.password: + return mock_exit(output=mock_entry.get('password', '')) + elif args.username: + return mock_exit(output=mock_entry.get('username', '')) + elif args.url: + return mock_exit(output=mock_entry.get('url', '')) + elif args.name: + return mock_exit(output=mock_entry.get('name', '')) + elif args.id: + return mock_exit(output=mock_entry.get('id', '')) + elif args.notes: + return mock_exit(output=mock_entry.get('notes', '')) + + raise LPassException('We should never get here') + + +class DisconnectedMockLPass(MockLPass): + + _mock_disconnected = True + + +class LoggedOutMockLPass(MockLPass): + + _mock_logged_out = True + + +class TestLPass(unittest.TestCase): + + def setUp(self): + self.lookup = lookup_loader.get('community.general.lastpass') + + def test_lastpass_cli_path(self): + lp = MockLPass(path='/dev/null') + self.assertEqual('/dev/null', lp.cli_path) + + def test_lastpass_build_args_logout(self): + lp = MockLPass() + self.assertEqual(['logout', '--color=never'], lp._build_args("logout")) + + def test_lastpass_logged_in_true(self): + lp = MockLPass() + self.assertTrue(lp.logged_in) + + def test_lastpass_logged_in_false(self): + lp = LoggedOutMockLPass() + self.assertFalse(lp.logged_in) + + def test_lastpass_show_disconnected(self): + lp = DisconnectedMockLPass() + + with self.assertRaises(LPassException): + lp.get_field('0123456789', 'username') + + def test_lastpass_show(self): + lp = MockLPass() + for entry in MOCK_ENTRIES: + entry_id = entry.get('id') + for k, v in six.iteritems(entry): + self.assertEqual(v.strip(), lp.get_field(entry_id, k)) + + +class TestLastpassPlugin(unittest.TestCase): + + def setUp(self): + self.lookup = lookup_loader.get('community.general.lastpass') + + @patch('ansible_collections.community.general.plugins.lookup.lastpass.LPass', new=MockLPass) + def test_lastpass_plugin_normal(self): + for entry in MOCK_ENTRIES: + entry_id = entry.get('id') + for k, v in six.iteritems(entry): + self.assertEqual(v.strip(), + self.lookup.run([entry_id], field=k)[0]) + + @patch('ansible_collections.community.general.plugins.lookup.lastpass.LPass', LoggedOutMockLPass) + def test_lastpass_plugin_logged_out(self): + entry = MOCK_ENTRIES[0] + entry_id = entry.get('id') + with self.assertRaises(AnsibleError): + self.lookup.run([entry_id], field='password') + + @patch('ansible_collections.community.general.plugins.lookup.lastpass.LPass', DisconnectedMockLPass) + def test_lastpass_plugin_disconnected(self): + entry = MOCK_ENTRIES[0] + entry_id = entry.get('id') + with self.assertRaises(AnsibleError): + self.lookup.run([entry_id], field='password') diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_manifold.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_manifold.py new file mode 100644 index 000000000..4fa356276 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_manifold.py @@ -0,0 +1,537 @@ +# Copyright (c) 2018, Arigato Machine Inc. +# Copyright (c) 2018, 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, call +from ansible.errors import AnsibleError +from ansible.module_utils.urls import ConnectionError, SSLValidationError +from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError +from ansible.module_utils import six +from ansible.plugins.loader import lookup_loader +from ansible_collections.community.general.plugins.lookup.manifold import ManifoldApiClient, ApiError +import json +import os + + +API_FIXTURES = { + 'https://api.marketplace.manifold.co/v1/resources': + [ + { + "body": { + "label": "resource-1", + "name": "Resource 1" + }, + "id": "rid-1" + }, + { + "body": { + "label": "resource-2", + "name": "Resource 2" + }, + "id": "rid-2" + } + ], + 'https://api.marketplace.manifold.co/v1/resources?label=resource-1': + [ + { + "body": { + "label": "resource-1", + "name": "Resource 1" + }, + "id": "rid-1" + } + ], + 'https://api.marketplace.manifold.co/v1/resources?label=resource-2': + [ + { + "body": { + "label": "resource-2", + "name": "Resource 2" + }, + "id": "rid-2" + } + ], + 'https://api.marketplace.manifold.co/v1/resources?team_id=tid-1': + [ + { + "body": { + "label": "resource-1", + "name": "Resource 1" + }, + "id": "rid-1" + } + ], + 'https://api.marketplace.manifold.co/v1/resources?project_id=pid-1': + [ + { + "body": { + "label": "resource-2", + "name": "Resource 2" + }, + "id": "rid-2" + } + ], + 'https://api.marketplace.manifold.co/v1/resources?project_id=pid-2': + [ + { + "body": { + "label": "resource-1", + "name": "Resource 1" + }, + "id": "rid-1" + }, + { + "body": { + "label": "resource-3", + "name": "Resource 3" + }, + "id": "rid-3" + } + ], + 'https://api.marketplace.manifold.co/v1/resources?team_id=tid-1&project_id=pid-1': + [ + { + "body": { + "label": "resource-1", + "name": "Resource 1" + }, + "id": "rid-1" + } + ], + 'https://api.marketplace.manifold.co/v1/projects': + [ + { + "body": { + "label": "project-1", + "name": "Project 1", + }, + "id": "pid-1", + }, + { + "body": { + "label": "project-2", + "name": "Project 2", + }, + "id": "pid-2", + } + ], + 'https://api.marketplace.manifold.co/v1/projects?label=project-2': + [ + { + "body": { + "label": "project-2", + "name": "Project 2", + }, + "id": "pid-2", + } + ], + 'https://api.marketplace.manifold.co/v1/credentials?resource_id=rid-1': + [ + { + "body": { + "resource_id": "rid-1", + "values": { + "RESOURCE_TOKEN_1": "token-1", + "RESOURCE_TOKEN_2": "token-2" + } + }, + "id": "cid-1", + } + ], + 'https://api.marketplace.manifold.co/v1/credentials?resource_id=rid-2': + [ + { + "body": { + "resource_id": "rid-2", + "values": { + "RESOURCE_TOKEN_3": "token-3", + "RESOURCE_TOKEN_4": "token-4" + } + }, + "id": "cid-2", + } + ], + 'https://api.marketplace.manifold.co/v1/credentials?resource_id=rid-3': + [ + { + "body": { + "resource_id": "rid-3", + "values": { + "RESOURCE_TOKEN_1": "token-5", + "RESOURCE_TOKEN_2": "token-6" + } + }, + "id": "cid-3", + } + ], + 'https://api.identity.manifold.co/v1/teams': + [ + { + "id": "tid-1", + "body": { + "name": "Team 1", + "label": "team-1" + } + }, + { + "id": "tid-2", + "body": { + "name": "Team 2", + "label": "team-2" + } + } + ] +} + + +def mock_fixture(open_url_mock, fixture=None, data=None, headers=None): + if not headers: + headers = {} + if fixture: + data = json.dumps(API_FIXTURES[fixture]) + if 'content-type' not in headers: + headers['content-type'] = 'application/json' + + open_url_mock.return_value.read.return_value = data + open_url_mock.return_value.headers = headers + + +class TestManifoldApiClient(unittest.TestCase): + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_sends_default_headers(self, open_url_mock): + mock_fixture(open_url_mock, data='hello') + client = ManifoldApiClient('token-123') + client.request('test', 'endpoint') + open_url_mock.assert_called_with('https://api.test.manifold.co/v1/endpoint', + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_decodes_json(self, open_url_mock): + mock_fixture(open_url_mock, fixture='https://api.marketplace.manifold.co/v1/resources') + client = ManifoldApiClient('token-123') + self.assertIsInstance(client.request('marketplace', 'resources'), list) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_streams_text(self, open_url_mock): + mock_fixture(open_url_mock, data='hello', headers={'content-type': "text/plain"}) + client = ManifoldApiClient('token-123') + self.assertEqual('hello', client.request('test', 'endpoint')) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_processes_parameterized_headers(self, open_url_mock): + mock_fixture(open_url_mock, data='hello') + client = ManifoldApiClient('token-123') + client.request('test', 'endpoint', headers={'X-HEADER': 'MANIFOLD'}) + open_url_mock.assert_called_with('https://api.test.manifold.co/v1/endpoint', + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123', + 'X-HEADER': 'MANIFOLD'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_passes_arbitrary_parameters(self, open_url_mock): + mock_fixture(open_url_mock, data='hello') + client = ManifoldApiClient('token-123') + client.request('test', 'endpoint', use_proxy=False, timeout=5) + open_url_mock.assert_called_with('https://api.test.manifold.co/v1/endpoint', + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0', + use_proxy=False, timeout=5) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_raises_on_incorrect_json(self, open_url_mock): + mock_fixture(open_url_mock, data='noJson', headers={'content-type': "application/json"}) + client = ManifoldApiClient('token-123') + with self.assertRaises(ApiError) as context: + client.request('test', 'endpoint') + self.assertEqual('JSON response can\'t be parsed while requesting https://api.test.manifold.co/v1/endpoint:\n' + 'noJson', + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_raises_on_status_500(self, open_url_mock): + open_url_mock.side_effect = HTTPError('https://api.test.manifold.co/v1/endpoint', + 500, 'Server error', {}, six.StringIO('ERROR')) + client = ManifoldApiClient('token-123') + with self.assertRaises(ApiError) as context: + client.request('test', 'endpoint') + self.assertEqual('Server returned: HTTP Error 500: Server error while requesting ' + 'https://api.test.manifold.co/v1/endpoint:\nERROR', + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_raises_on_bad_url(self, open_url_mock): + open_url_mock.side_effect = URLError('URL is invalid') + client = ManifoldApiClient('token-123') + with self.assertRaises(ApiError) as context: + client.request('test', 'endpoint') + self.assertEqual('Failed lookup url for https://api.test.manifold.co/v1/endpoint : ', + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_raises_on_ssl_error(self, open_url_mock): + open_url_mock.side_effect = SSLValidationError('SSL Error') + client = ManifoldApiClient('token-123') + with self.assertRaises(ApiError) as context: + client.request('test', 'endpoint') + self.assertEqual('Error validating the server\'s certificate for https://api.test.manifold.co/v1/endpoint: ' + 'SSL Error', + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_request_raises_on_connection_error(self, open_url_mock): + open_url_mock.side_effect = ConnectionError('Unknown connection error') + client = ManifoldApiClient('token-123') + with self.assertRaises(ApiError) as context: + client.request('test', 'endpoint') + self.assertEqual('Error connecting to https://api.test.manifold.co/v1/endpoint: Unknown connection error', + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_resources_get_all(self, open_url_mock): + url = 'https://api.marketplace.manifold.co/v1/resources' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_resources()) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_resources_filter_label(self, open_url_mock): + url = 'https://api.marketplace.manifold.co/v1/resources?label=resource-1' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_resources(label='resource-1')) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_resources_filter_team_and_project(self, open_url_mock): + url = 'https://api.marketplace.manifold.co/v1/resources?team_id=tid-1&project_id=pid-1' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_resources(team_id='tid-1', project_id='pid-1')) + args, kwargs = open_url_mock.call_args + url_called = args[0] + # Dict order is not guaranteed, so an url may have querystring parameters order randomized + self.assertIn('team_id=tid-1', url_called) + self.assertIn('project_id=pid-1', url_called) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_teams_get_all(self, open_url_mock): + url = 'https://api.identity.manifold.co/v1/teams' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_teams()) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_teams_filter_label(self, open_url_mock): + url = 'https://api.identity.manifold.co/v1/teams' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url][1:2], client.get_teams(label='team-2')) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_projects_get_all(self, open_url_mock): + url = 'https://api.marketplace.manifold.co/v1/projects' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_projects()) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_projects_filter_label(self, open_url_mock): + url = 'https://api.marketplace.manifold.co/v1/projects?label=project-2' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_projects(label='project-2')) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.open_url') + def test_get_credentials(self, open_url_mock): + url = 'https://api.marketplace.manifold.co/v1/credentials?resource_id=rid-1' + mock_fixture(open_url_mock, fixture=url) + client = ManifoldApiClient('token-123') + self.assertListEqual(API_FIXTURES[url], client.get_credentials(resource_id='rid-1')) + open_url_mock.assert_called_with(url, + headers={'Accept': '*/*', 'Authorization': 'Bearer token-123'}, + http_agent='python-manifold-ansible-1.0.0') + + +class TestLookupModule(unittest.TestCase): + def setUp(self): + self.lookup = lookup_loader.get('community.general.manifold') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_get_all(self, client_mock): + expected_result = [{'RESOURCE_TOKEN_1': 'token-1', + 'RESOURCE_TOKEN_2': 'token-2', + 'RESOURCE_TOKEN_3': 'token-3', + 'RESOURCE_TOKEN_4': 'token-4' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources'] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run([], api_token='token-123')) + client_mock.assert_called_with('token-123') + client_mock.return_value.get_resources.assert_called_with(team_id=None, project_id=None) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_get_one_resource(self, client_mock): + expected_result = [{'RESOURCE_TOKEN_3': 'token-3', + 'RESOURCE_TOKEN_4': 'token-4' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources?label=resource-2'] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run(['resource-2'], api_token='token-123')) + client_mock.return_value.get_resources.assert_called_with(team_id=None, project_id=None, label='resource-2') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_get_two_resources(self, client_mock): + expected_result = [{'RESOURCE_TOKEN_1': 'token-1', + 'RESOURCE_TOKEN_2': 'token-2', + 'RESOURCE_TOKEN_3': 'token-3', + 'RESOURCE_TOKEN_4': 'token-4' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources'] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run(['resource-1', 'resource-2'], api_token='token-123')) + client_mock.assert_called_with('token-123') + client_mock.return_value.get_resources.assert_called_with(team_id=None, project_id=None) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.display') + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_get_resources_with_same_credential_names(self, client_mock, display_mock): + expected_result = [{'RESOURCE_TOKEN_1': 'token-5', + 'RESOURCE_TOKEN_2': 'token-6' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources?project_id=pid-2'] + client_mock.return_value.get_projects.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/projects?label=project-2'] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run([], api_token='token-123', project='project-2')) + client_mock.assert_called_with('token-123') + display_mock.warning.assert_has_calls([ + call("'RESOURCE_TOKEN_1' with label 'resource-1' was replaced by resource data with label 'resource-3'"), + call("'RESOURCE_TOKEN_2' with label 'resource-1' was replaced by resource data with label 'resource-3'")], + any_order=True + ) + client_mock.return_value.get_resources.assert_called_with(team_id=None, project_id='pid-2') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_filter_by_team(self, client_mock): + expected_result = [{'RESOURCE_TOKEN_1': 'token-1', + 'RESOURCE_TOKEN_2': 'token-2' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources?team_id=tid-1'] + client_mock.return_value.get_teams.return_value = API_FIXTURES['https://api.identity.manifold.co/v1/teams'][0:1] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run([], api_token='token-123', team='team-1')) + client_mock.assert_called_with('token-123') + client_mock.return_value.get_resources.assert_called_with(team_id='tid-1', project_id=None) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_filter_by_project(self, client_mock): + expected_result = [{'RESOURCE_TOKEN_3': 'token-3', + 'RESOURCE_TOKEN_4': 'token-4' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources?project_id=pid-1'] + client_mock.return_value.get_projects.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/projects'][0:1] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run([], api_token='token-123', project='project-1')) + client_mock.assert_called_with('token-123') + client_mock.return_value.get_resources.assert_called_with(team_id=None, project_id='pid-1') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_filter_by_team_and_project(self, client_mock): + expected_result = [{'RESOURCE_TOKEN_1': 'token-1', + 'RESOURCE_TOKEN_2': 'token-2' + }] + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources?team_id=tid-1&project_id=pid-1'] + client_mock.return_value.get_teams.return_value = API_FIXTURES['https://api.identity.manifold.co/v1/teams'][0:1] + client_mock.return_value.get_projects.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/projects'][0:1] + client_mock.return_value.get_credentials.side_effect = lambda x: API_FIXTURES['https://api.marketplace.manifold.co/v1/' + 'credentials?resource_id={0}'.format(x)] + self.assertListEqual(expected_result, self.lookup.run([], api_token='token-123', project='project-1')) + client_mock.assert_called_with('token-123') + client_mock.return_value.get_resources.assert_called_with(team_id=None, project_id='pid-1') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_raise_team_doesnt_exist(self, client_mock): + client_mock.return_value.get_teams.return_value = [] + with self.assertRaises(AnsibleError) as context: + self.lookup.run([], api_token='token-123', team='no-team') + self.assertEqual("Team 'no-team' does not exist", + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_raise_project_doesnt_exist(self, client_mock): + client_mock.return_value.get_projects.return_value = [] + with self.assertRaises(AnsibleError) as context: + self.lookup.run([], api_token='token-123', project='no-project') + self.assertEqual("Project 'no-project' does not exist", + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_raise_resource_doesnt_exist(self, client_mock): + client_mock.return_value.get_resources.return_value = API_FIXTURES['https://api.marketplace.manifold.co/v1/resources'] + with self.assertRaises(AnsibleError) as context: + self.lookup.run(['resource-1', 'no-resource-1', 'no-resource-2'], api_token='token-123') + self.assertEqual("Resource(s) no-resource-1, no-resource-2 do not exist", + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_catch_api_error(self, client_mock): + client_mock.side_effect = ApiError('Generic error') + with self.assertRaises(AnsibleError) as context: + self.lookup.run([], api_token='token-123') + self.assertEqual("API Error: Generic error", + str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_catch_unhandled_exception(self, client_mock): + client_mock.side_effect = Exception('Unknown error') + with self.assertRaises(AnsibleError) as context: + self.lookup.run([], api_token='token-123') + self.assertTrue('Exception: Unknown error' in str(context.exception)) + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_falls_back_to_env_var(self, client_mock): + client_mock.return_value.get_resources.return_value = [] + client_mock.return_value.get_credentials.return_value = [] + try: + os.environ['MANIFOLD_API_TOKEN'] = 'token-321' + self.lookup.run([]) + finally: + os.environ.pop('MANIFOLD_API_TOKEN', None) + client_mock.assert_called_with('token-321') + + @patch('ansible_collections.community.general.plugins.lookup.manifold.ManifoldApiClient') + def test_falls_raises_on_no_token(self, client_mock): + client_mock.return_value.get_resources.return_value = [] + client_mock.return_value.get_credentials.return_value = [] + os.environ.pop('MANIFOLD_API_TOKEN', None) + with self.assertRaises(AnsibleError) as context: + self.lookup.run([]) + assert 'api_token' in str(context.exception) 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 new file mode 100644 index 000000000..5085797b3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +# 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 +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_collections.community.general.tests.unit.mock.loader import DictDataLoader + +from ansible.plugins import AnsiblePlugin +from ansible.template import Templar +from ansible.errors import AnsibleError +from ansible.utils.display import Display +from ansible_collections.community.general.plugins.lookup import merge_variables + + +class TestMergeVariablesLookup(unittest.TestCase): + def setUp(self): + self.loader = DictDataLoader({}) + self.templar = Templar(loader=self.loader, variables={}) + 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(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'], { + 'testlist1__merge_list': ['item1'], + 'testlist2': ['item2'], + 'testlist3__merge_list': ['item3'] + }) + + self.assertEqual(results, [['item1', 'item3']]) + + @patch.object(AnsiblePlugin, 'set_options') + @patch.object(AnsiblePlugin, 'get_option', side_effect=[['initial_item'], 'ignore', 'suffix']) + @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'], { + 'testlist1__merge_list': ['item1'], + 'testlist2': ['item2'], + 'testlist3__merge_list': ['item3'] + }) + + self.assertEqual(results, [['initial_item', 'item1', 'item3']]) + + @patch.object(AnsiblePlugin, 'set_options') + @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix']) + @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): + results = self.merge_vars_lookup.run(['__merge_dict'], { + 'testdict1__merge_dict': { + 'item1': 'test', + 'list_item': ['test1'] + }, + 'testdict2__merge_dict': { + 'item2': 'test', + 'list_item': ['test2'] + } + }) + + self.assertEqual(results, [ + { + 'item1': 'test', + 'item2': 'test', + 'list_item': ['test1', 'test2'] + } + ]) + + @patch.object(AnsiblePlugin, 'set_options') + @patch.object(AnsiblePlugin, 'get_option', side_effect=[{'initial_item': 'random value', 'list_item': ['test0']}, + 'ignore', 'suffix']) + @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): + results = self.merge_vars_lookup.run(['__merge_dict'], { + 'testdict1__merge_dict': { + 'item1': 'test', + 'list_item': ['test1'] + }, + 'testdict2__merge_dict': { + 'item2': 'test', + 'list_item': ['test2'] + } + }) + + self.assertEqual(results, [ + { + 'initial_item': 'random value', + 'item1': 'test', + 'item2': 'test', + 'list_item': ['test0', 'test1', 'test2'] + } + ]) + + @patch.object(AnsiblePlugin, 'set_options') + @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'warn', 'suffix']) + @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): + results = self.merge_vars_lookup.run(['__merge_non_unique'], { + 'testdict1__merge_non_unique': {'item': 'value1'}, + 'testdict2__merge_non_unique': {'item': 'value2'} + }) + + self.assertTrue(mock_display.called) + self.assertEqual(results, [{'item': 'value2'}]) + + @patch.object(AnsiblePlugin, 'set_options') + @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'error', 'suffix']) + @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): + self.merge_vars_lookup.run(['__merge_non_unique'], { + 'testdict1__merge_non_unique': {'item': 'value1'}, + 'testdict2__merge_non_unique': {'item': 'value2'} + }) + + @patch.object(AnsiblePlugin, 'set_options') + @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix']) + @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): + with self.assertRaises(AnsibleError): + self.merge_vars_lookup.run(['__merge_var'], { + 'testlist__merge_var': { + 'item1': 'test', + 'list_item': ['test1'] + }, + 'testdict__merge_var': ['item2', 'item3'] + }) 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 new file mode 100644 index 000000000..ab7f3def2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py @@ -0,0 +1,268 @@ +# Copyright (c) 2022 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 operator +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.plugins.loader import lookup_loader +from ansible_collections.community.general.plugins.lookup.onepassword import ( + OnePassCLIv1, + OnePassCLIv2, +) + + +@pytest.mark.parametrize( + ("args", "rc", "expected_call_args", "expected_call_kwargs", "expected"), + ( + ([], 0, ["get", "account"], {"ignore_errors": True}, True,), + ([], 1, ["get", "account"], {"ignore_errors": True}, False,), + (["acme"], 1, ["get", "account", "--account", "acme.1password.com"], {"ignore_errors": True}, False,), + ) +) +def test_assert_logged_in_v1(mocker, args, rc, expected_call_args, expected_call_kwargs, expected): + mocker.patch.object(OnePassCLIv1, "_run", return_value=[rc, "", ""]) + + op_cli = OnePassCLIv1(*args) + result = op_cli.assert_logged_in() + + op_cli._run.assert_called_with(expected_call_args, **expected_call_kwargs) + assert result == expected + + +def test_full_signin_v1(mocker): + mocker.patch.object(OnePassCLIv1, "_run", return_value=[0, "", ""]) + + op_cli = OnePassCLIv1( + subdomain="acme", + username="bob@acme.com", + secret_key="SECRET", + master_password="ONEKEYTORULETHEMALL", + ) + result = op_cli.full_signin() + + op_cli._run.assert_called_with([ + "signin", + "acme.1password.com", + b"bob@acme.com", + b"SECRET", + "--raw", + ], command_input=b"ONEKEYTORULETHEMALL") + assert result == [0, "", ""] + + +@pytest.mark.parametrize( + ("args", "out", "expected_call_args", "expected_call_kwargs", "expected"), + ( + ([], "list of accounts", ["account", "get"], {"ignore_errors": True}, True,), + (["acme"], "list of accounts", ["account", "get", "--account", "acme.1password.com"], {"ignore_errors": True}, True,), + ([], "", ["account", "list"], {}, False,), + ) +) +def test_assert_logged_in_v2(mocker, args, out, expected_call_args, expected_call_kwargs, expected): + mocker.patch.object(OnePassCLIv2, "_run", return_value=[0, out, ""]) + op_cli = OnePassCLIv2(*args) + result = op_cli.assert_logged_in() + + op_cli._run.assert_called_with(expected_call_args, **expected_call_kwargs) + assert result == expected + + +def test_full_signin_v2(mocker): + mocker.patch.object(OnePassCLIv2, "_run", return_value=[0, "", ""]) + + op_cli = OnePassCLIv2( + subdomain="acme", + username="bob@acme.com", + secret_key="SECRET", + master_password="ONEKEYTORULETHEMALL", + ) + result = op_cli.full_signin() + + op_cli._run.assert_called_with( + [ + "account", "add", "--raw", + "--address", "acme.1password.com", + "--email", b"bob@acme.com", + "--signin", + ], + command_input=b"ONEKEYTORULETHEMALL", + environment_update={'OP_SECRET_KEY': 'SECRET'}, + ) + assert result == [0, "", ""] + + +@pytest.mark.parametrize( + ("version", "version_class"), + ( + ("1.17.2", OnePassCLIv1), + ("2.27.4", OnePassCLIv2), + ) +) +def test_op_correct_cli_class(fake_op, version, version_class): + op = fake_op(version) + assert op._cli.version == version + assert isinstance(op._cli, version_class) + + +def test_op_unsupported_cli_version(fake_op): + with pytest.raises(AnsibleLookupError, match="is unsupported"): + fake_op("99.77.77") + + +@pytest.mark.parametrize("op_fixture", OP_VERSION_FIXTURES) +def test_op_set_token_with_config(op_fixture, mocker, request): + op = request.getfixturevalue(op_fixture) + token = "F5417F77529B41B595D7F9D6F76EC057" + mocker.patch("os.path.isfile", return_value=True) + mocker.patch.object(op._cli, "signin", return_value=(0, token + "\n", "")) + + op.set_token() + + assert op.token == token + + +@pytest.mark.parametrize( + ("op_fixture", "message"), + [ + (op, value) + for op in OP_VERSION_FIXTURES + for value in + ( + "Missing required parameters", + "The operation is unauthorized", + ) + ] +) +def test_op_set_token_with_config_missing_args(op_fixture, message, request, mocker): + op = request.getfixturevalue(op_fixture) + mocker.patch("os.path.isfile", return_value=True) + mocker.patch.object(op._cli, "signin", return_value=(99, "", ""), side_effect=AnsibleLookupError(message)) + mocker.patch.object(op._cli, "full_signin", return_value=(0, "", "")) + + with pytest.raises(AnsibleLookupError, match=message): + op.set_token() + + op._cli.full_signin.assert_not_called() + + +@pytest.mark.parametrize("op_fixture", OP_VERSION_FIXTURES) +def test_op_set_token_with_config_full_signin(op_fixture, request, mocker): + op = request.getfixturevalue(op_fixture) + mocker.patch("os.path.isfile", return_value=True) + mocker.patch.object(op._cli, "signin", return_value=(99, "", ""), side_effect=AnsibleLookupError("Raised intentionally")) + mocker.patch.object(op._cli, "full_signin", return_value=(0, "", "")) + + op.set_token() + + op._cli.full_signin.assert_called() + + +@pytest.mark.parametrize("op_fixture", OP_VERSION_FIXTURES) +def test_op_set_token_without_config(op_fixture, request, mocker): + op = request.getfixturevalue(op_fixture) + token = "B988E8A2680A4A348962751A96861FA1" + mocker.patch("os.path.isfile", return_value=False) + mocker.patch.object(op._cli, "signin", return_value=(99, "", "")) + mocker.patch.object(op._cli, "full_signin", return_value=(0, token + "\n", "")) + + op.set_token() + + op._cli.signin.assert_not_called() + assert op.token == token + + +@pytest.mark.parametrize( + ("op_fixture", "login_status"), + [(op, value) for op in OP_VERSION_FIXTURES for value in [False, True]] +) +def test_op_assert_logged_in(mocker, login_status, op_fixture, request): + op = request.getfixturevalue(op_fixture) + mocker.patch.object(op._cli, "assert_logged_in", return_value=login_status) + mocker.patch.object(op, "set_token") + + op.assert_logged_in() + + op._cli.assert_logged_in.assert_called_once() + assert op.logged_in == login_status + + if not login_status: + op.set_token.assert_called_once() + + +@pytest.mark.parametrize("op_fixture", OP_VERSION_FIXTURES) +def test_op_get_raw_v1(mocker, op_fixture, request): + op = request.getfixturevalue(op_fixture) + mocker.patch.object(op._cli, "get_raw", return_value=[99, "RAW OUTPUT", ""]) + + result = op.get_raw("some item") + + assert result == "RAW OUTPUT" + op._cli.get_raw.assert_called_once() + + +@pytest.mark.parametrize( + ("op_fixture", "output", "expected"), + ( + list(itertools.chain([op], d)) + for op in OP_VERSION_FIXTURES + for d in [ + ("RAW OUTPUT", "RAW OUTPUT"), + (None, ""), + ("", ""), + ] + ) +) +def test_op_get_field(mocker, op_fixture, output, expected, request): + op = request.getfixturevalue(op_fixture) + mocker.patch.object(op, "get_raw", return_value=output) + mocker.patch.object(op._cli, "_parse_field", return_value=output) + + result = op.get_field("some item", "some field") + + assert result == expected + + +# This test sometimes fails on older Python versions because the gathered tests mismatch. +# Sort the fixture data to make this reliable +# https://github.com/pytest-dev/pytest-xdist/issues/432 +@pytest.mark.parametrize( + ("cli_class", "vault", "queries", "kwargs", "output", "expected"), + ( + (_cli_class, item["vault_name"], item["queries"], item.get("kwargs", {}), item["output"], item["expected"]) + for _cli_class in sorted(MOCK_ENTRIES, key=operator.attrgetter("__name__")) + for item in MOCK_ENTRIES[_cli_class] + ) +) +def test_op_lookup(mocker, cli_class, vault, queries, kwargs, output, expected): + mocker.patch("ansible_collections.community.general.plugins.lookup.onepassword.OnePass._get_cli_class", cli_class) + 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, json.dumps(output), "")) + + op_lookup = lookup_loader.get("community.general.onepassword") + result = op_lookup.run(queries, vault=vault, **kwargs) + + assert result == expected + + +@pytest.mark.parametrize("op_fixture", OP_VERSION_FIXTURES) +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") diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_revbitspss.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_revbitspss.py new file mode 100644 index 000000000..510999206 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_revbitspss.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, RevBits +# 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.unittest import TestCase +from ansible_collections.community.general.tests.unit.compat.mock import ( + patch, + MagicMock, +) +from ansible_collections.community.general.plugins.lookup import revbitspss +from ansible.plugins.loader import lookup_loader + + +class MockPamSecrets(MagicMock): + RESPONSE = 'dummy value' + + def get_pam_secret(self, path): + return self.RESPONSE + + +class TestLookupModule(TestCase): + def setUp(self): + revbitspss.ANOTHER_LIBRARY_IMPORT_ERROR = None + self.lookup = lookup_loader.get("community.general.revbitspss") + + @patch( + "ansible_collections.community.general.plugins.lookup.revbitspss.LookupModule.Client", + MockPamSecrets(), + ) + def test_get_pam_secret(self): + terms = ['dummy secret'] + variables = [] + kwargs = { + "base_url": 'https://dummy.url', + "api_key": 'dummy' + } + self.assertListEqual( + [{'dummy secret': 'dummy value'}], + self.lookup.run(terms, variables, **kwargs) + ) diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_tss.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_tss.py new file mode 100644 index 000000000..47ca79a69 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_tss.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Adam Migus +# 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 + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible_collections.community.general.tests.unit.compat.unittest import TestCase +from ansible_collections.community.general.tests.unit.compat.mock import ( + patch, + DEFAULT, + MagicMock, +) +from ansible_collections.community.general.plugins.lookup import tss +from ansible.plugins.loader import lookup_loader + + +TSS_IMPORT_PATH = 'ansible_collections.community.general.plugins.lookup.tss' + + +def make_absolute(name): + return '.'.join([TSS_IMPORT_PATH, name]) + + +class SecretServerError(Exception): + def __init__(self): + self.message = '' + + +class MockSecretServer(MagicMock): + RESPONSE = '{"foo": "bar"}' + + def get_secret_json(self, path): + return self.RESPONSE + + +class MockFaultySecretServer(MagicMock): + def get_secret_json(self, path): + raise SecretServerError + + +@patch(make_absolute('SecretServer'), MockSecretServer()) +class TestTSSClient(TestCase): + def setUp(self): + self.server_params = { + 'base_url': '', + 'username': '', + 'domain': '', + 'password': '', + 'api_path_uri': '', + 'token_path_uri': '', + } + + def test_from_params(self): + with patch(make_absolute('HAS_TSS_AUTHORIZER'), False): + self.assert_client_version('v0') + + with patch.dict(self.server_params, {'domain': 'foo'}): + with self.assertRaises(tss.AnsibleError): + self._get_client() + + with patch.multiple(TSS_IMPORT_PATH, + HAS_TSS_AUTHORIZER=True, + PasswordGrantAuthorizer=DEFAULT, + DomainPasswordGrantAuthorizer=DEFAULT): + + self.assert_client_version('v1') + + with patch.dict(self.server_params, {'domain': 'foo'}): + self.assert_client_version('v1') + + def assert_client_version(self, version): + version_to_class = { + 'v0': tss.TSSClientV0, + 'v1': tss.TSSClientV1 + } + + client = self._get_client() + self.assertIsInstance(client, version_to_class[version]) + + def _get_client(self): + return tss.TSSClient.from_params(**self.server_params) + + +class TestLookupModule(TestCase): + VALID_TERMS = [1] + INVALID_TERMS = ['foo'] + + def setUp(self): + self.lookup = lookup_loader.get("community.general.tss") + + @patch.multiple(TSS_IMPORT_PATH, + HAS_TSS_SDK=False, + SecretServer=MockSecretServer) + def test_missing_sdk(self): + with self.assertRaises(tss.AnsibleError): + self._run_lookup(self.VALID_TERMS) + + @patch.multiple(TSS_IMPORT_PATH, + HAS_TSS_SDK=True, + SecretServerError=SecretServerError) + def test_get_secret_json(self): + with patch(make_absolute('SecretServer'), MockSecretServer): + self.assertListEqual([MockSecretServer.RESPONSE], self._run_lookup(self.VALID_TERMS)) + + with self.assertRaises(tss.AnsibleOptionsError): + self._run_lookup(self.INVALID_TERMS) + + with patch(make_absolute('SecretServer'), MockFaultySecretServer): + with self.assertRaises(tss.AnsibleError): + self._run_lookup(self.VALID_TERMS) + + def _run_lookup(self, terms, variables=None, **kwargs): + variables = variables or [] + kwargs = kwargs or {"base_url": "dummy", "username": "dummy", "password": "dummy"} + + return self.lookup.run(terms, variables, **kwargs) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_backoff.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_backoff.py new file mode 100644 index 000000000..5a5188669 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_backoff.py @@ -0,0 +1,54 @@ +# 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 random + +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.plugins.module_utils.cloud import _exponential_backoff, \ + _full_jitter_backoff + + +class ExponentialBackoffStrategyTestCase(unittest.TestCase): + def test_no_retries(self): + strategy = _exponential_backoff(retries=0) + result = list(strategy()) + self.assertEqual(result, [], 'list should be empty') + + def test_exponential_backoff(self): + strategy = _exponential_backoff(retries=5, delay=1, backoff=2) + result = list(strategy()) + self.assertEqual(result, [1, 2, 4, 8, 16]) + + def test_max_delay(self): + strategy = _exponential_backoff(retries=7, delay=1, backoff=2, max_delay=60) + result = list(strategy()) + self.assertEqual(result, [1, 2, 4, 8, 16, 32, 60]) + + def test_max_delay_none(self): + strategy = _exponential_backoff(retries=7, delay=1, backoff=2, max_delay=None) + result = list(strategy()) + self.assertEqual(result, [1, 2, 4, 8, 16, 32, 64]) + + +class FullJitterBackoffStrategyTestCase(unittest.TestCase): + def test_no_retries(self): + strategy = _full_jitter_backoff(retries=0) + result = list(strategy()) + self.assertEqual(result, [], 'list should be empty') + + def test_full_jitter(self): + retries = 5 + seed = 1 + + r = random.Random(seed) + expected = [r.randint(0, 2**i) for i in range(0, retries)] + + strategy = _full_jitter_backoff( + retries=retries, delay=1, _random=random.Random(seed)) + result = list(strategy()) + + self.assertEqual(result, expected) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_scaleway.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_scaleway.py new file mode 100644 index 000000000..dc53bc126 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/cloud/test_scaleway.py @@ -0,0 +1,126 @@ +# 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.plugins.module_utils.scaleway import SecretVariables, argon2 + + +class SecretVariablesTestCase(unittest.TestCase): + def test_dict_to_list(self): + source = dict( + attribute1="value1", + attribute2="value2" + ) + expect = [ + dict(key="attribute1", value="value1"), + dict(key="attribute2", value="value2") + ] + + result = SecretVariables.dict_to_list(source) + result = sorted(result, key=lambda el: el['key']) + self.assertEqual(result, expect) + + def test_list_to_dict(self): + source = [ + dict(key="secret1", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$NuZk+6UATHNFV78nFRXFvA$3kivcXfzNHI1c/4ZBpP8BeBSGhhI82NfOh4Dd48JJgc"), + dict(key="secret2", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI") + ] + expect = dict( + secret1="$argon2id$v=19$m=65536,t=1,p=2$NuZk+6UATHNFV78nFRXFvA$3kivcXfzNHI1c/4ZBpP8BeBSGhhI82NfOh4Dd48JJgc", + secret2="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI" + ) + + self.assertEqual(SecretVariables.list_to_dict(source, hashed=True), expect) + + def test_list_to_dict(self): + source = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="value2") + ] + expect = dict( + secret1="value1", + secret2="value2" + ) + + self.assertEqual(SecretVariables.list_to_dict(source, hashed=False), expect) + + @unittest.skipIf(argon2 is None, "Missing required 'argon2' library") + def test_decode_full(self): + source_secret = [ + dict(key="secret1", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$NuZk+6UATHNFV78nFRXFvA$3kivcXfzNHI1c/4ZBpP8BeBSGhhI82NfOh4Dd48JJgc"), + dict(key="secret2", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI"), + ] + source_value = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="value2"), + ] + + expect = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="value2"), + ] + + result = SecretVariables.decode(source_secret, source_value) + result = sorted(result, key=lambda el: el['key']) + self.assertEqual(result, expect) + + @unittest.skipIf(argon2 is None, "Missing required 'argon2' library") + def test_decode_dict_divergent_values(self): + source_secret = [ + dict(key="secret1", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$NuZk+6UATHNFV78nFRXFvA$3kivcXfzNHI1c/4ZBpP8BeBSGhhI82NfOh4Dd48JJgc"), + dict(key="secret2", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI"), + ] + source_value = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="diverged_value2"), + ] + + expect = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI"), + ] + + result = SecretVariables.decode(source_secret, source_value) + result = sorted(result, key=lambda el: el['key']) + self.assertEqual(result, expect) + + @unittest.skipIf(argon2 is None, "Missing required 'argon2' library") + def test_decode_dict_missing_values_left(self): + source_secret = [ + dict(key="secret1", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$NuZk+6UATHNFV78nFRXFvA$3kivcXfzNHI1c/4ZBpP8BeBSGhhI82NfOh4Dd48JJgc"), + ] + source_value = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="value2"), + ] + + expect = [ + dict(key="secret1", value="value1"), + ] + + result = SecretVariables.decode(source_secret, source_value) + result = sorted(result, key=lambda el: el['key']) + self.assertEqual(result, expect) + + @unittest.skipIf(argon2 is None, "Missing required 'argon2' library") + def test_decode_dict_missing_values_right(self): + source_secret = [ + dict(key="secret1", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$NuZk+6UATHNFV78nFRXFvA$3kivcXfzNHI1c/4ZBpP8BeBSGhhI82NfOh4Dd48JJgc"), + dict(key="secret2", hashed_value="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI"), + ] + source_value = [ + dict(key="secret1", value="value1"), + ] + + expect = [ + dict(key="secret1", value="value1"), + dict(key="secret2", value="$argon2id$v=19$m=65536,t=1,p=2$etGO/Z8ImYDeKr6uFsyPAQ$FbL5+hG/duDEpa8UCYqXpEUQ5EacKg6i2iAs+Dq4dAI"), + ] + + result = SecretVariables.decode(source_secret, source_value) + result = sorted(result, key=lambda el: el['key']) + self.assertEqual(result, expect) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/conftest.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/conftest.py new file mode 100644 index 000000000..2217dd39f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/conftest.py @@ -0,0 +1,73 @@ +# Copyright (c) 2017 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 +import sys +from io import BytesIO + +import pytest + +import ansible.module_utils.basic +from ansible.module_utils.six import PY3, string_types +from ansible.module_utils.common.text.converters import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def stdin(mocker, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the stdin pytest fixture') + + fake_stdin = BytesIO(to_bytes(args, errors='surrogate_or_strict')) + if PY3: + mocker.patch('ansible.module_utils.basic.sys.stdin', mocker.MagicMock()) + mocker.patch('ansible.module_utils.basic.sys.stdin.buffer', fake_stdin) + else: + mocker.patch('ansible.module_utils.basic.sys.stdin', fake_stdin) + + yield fake_stdin + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv + + +@pytest.fixture +def am(stdin, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + argspec = {} + if hasattr(request, 'param'): + if isinstance(request.param, dict): + argspec = request.param + + am = ansible.module_utils.basic.AnsibleModule( + argument_spec=argspec, + ) + am._name = 'ansible_unittest' + + yield am + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_dict_comparison.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_dict_comparison.py new file mode 100644 index 000000000..037305d3f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_dict_comparison.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +# 2018.07.26 --- use DictComparison instead of GcpRequest +# +# Copyright (c) 2016, Tom Melendez +# +# 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.plugins.module_utils.hwc_utils import are_different_dicts + + +class HwcDictComparisonTestCase(unittest.TestCase): + def test_simple_no_difference(self): + value1 = { + 'foo': 'bar', + 'test': 'original' + } + + self.assertFalse(are_different_dicts(value1, value1)) + + def test_simple_different(self): + value1 = { + 'foo': 'bar', + 'test': 'original' + } + value2 = { + 'foo': 'bar', + 'test': 'different' + } + value3 = { + 'test': 'original' + } + + self.assertTrue(are_different_dicts(value1, value2)) + self.assertTrue(are_different_dicts(value1, value3)) + self.assertTrue(are_different_dicts(value2, value3)) + + def test_nested_dictionaries_no_difference(self): + value1 = { + 'foo': { + 'quiet': { + 'tree': 'test' + }, + 'bar': 'baz' + }, + 'test': 'original' + } + + self.assertFalse(are_different_dicts(value1, value1)) + + def test_nested_dictionaries_with_difference(self): + value1 = { + 'foo': { + 'quiet': { + 'tree': 'test' + }, + 'bar': 'baz' + }, + 'test': 'original' + } + value2 = { + 'foo': { + 'quiet': { + 'tree': 'baz' + }, + 'bar': 'hello' + }, + 'test': 'original' + } + value3 = { + 'foo': { + 'quiet': { + 'tree': 'test' + }, + 'bar': 'baz' + } + } + + self.assertTrue(are_different_dicts(value1, value2)) + self.assertTrue(are_different_dicts(value1, value3)) + self.assertTrue(are_different_dicts(value2, value3)) + + def test_arrays_strings_no_difference(self): + value1 = { + 'foo': [ + 'baz', + 'bar' + ] + } + + self.assertFalse(are_different_dicts(value1, value1)) + + def test_arrays_strings_with_difference(self): + value1 = { + 'foo': [ + 'baz', + 'bar', + ] + } + + value2 = { + 'foo': [ + 'baz', + 'hello' + ] + } + value3 = { + 'foo': [ + 'bar', + ] + } + + self.assertTrue(are_different_dicts(value1, value2)) + self.assertTrue(are_different_dicts(value1, value3)) + self.assertTrue(are_different_dicts(value2, value3)) + + def test_arrays_dicts_with_no_difference(self): + value1 = { + 'foo': [ + { + 'test': 'value', + 'foo': 'bar' + }, + { + 'different': 'dict' + } + ] + } + + self.assertFalse(are_different_dicts(value1, value1)) + + def test_arrays_dicts_with_difference(self): + value1 = { + 'foo': [ + { + 'test': 'value', + 'foo': 'bar' + }, + { + 'different': 'dict' + } + ] + } + value2 = { + 'foo': [ + { + 'test': 'value2', + 'foo': 'bar2' + }, + ] + } + value3 = { + 'foo': [ + { + 'test': 'value', + 'foo': 'bar' + } + ] + } + + self.assertTrue(are_different_dicts(value1, value2)) + self.assertTrue(are_different_dicts(value1, value3)) + self.assertTrue(are_different_dicts(value2, value3)) 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 new file mode 100644 index 000000000..1344496b1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py @@ -0,0 +1,40 @@ +# -*- 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 + +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 test_navigate_value(self): + value = { + 'foo': { + 'quiet': { + 'tree': 'test', + "trees": [0, 1] + }, + } + } + + self.assertEqual(navigate_value(value, ["foo", "quiet", "tree"]), + "test") + + self.assertEqual( + navigate_value(value, ["foo", "quiet", "trees"], + {"foo.quiet.trees": 1}), + 1) + + self.assertRaisesRegexp(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}) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_connect.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_connect.py new file mode 100644 index 000000000..9a816cfe2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_connect.py @@ -0,0 +1,165 @@ +# 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 pytest +from itertools import count + +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import ( + get_token, + KeycloakError, +) +from ansible.module_utils.six import StringIO +from ansible.module_utils.six.moves.urllib.error import HTTPError + +module_params_creds = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'validate_certs': True, + 'auth_realm': 'master', + 'client_id': 'admin-cli', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'client_secret': None, +} + + +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 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): + try: + call_number = get_id_call_count.__next__() + except AttributeError: + # manage python 2 versions. + call_number = get_id_call_count.next() + return get_response( + object_with_future_response[call_number], method, get_id_call_count) + return object_with_future_response + + +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 + + +@pytest.fixture() +def mock_good_connection(mocker): + token_response = { + 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token': create_wrapper('{"access_token": "alongtoken"}'), } + return mocker.patch( + 'ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak.open_url', + side_effect=build_mocked_request(count(), token_response), + autospec=True + ) + + +def test_connect_to_keycloak_with_creds(mock_good_connection): + keycloak_header = get_token(module_params_creds) + assert keycloak_header == { + 'Authorization': 'Bearer alongtoken', + 'Content-Type': 'application/json' + } + + +def test_connect_to_keycloak_with_token(mock_good_connection): + module_params_token = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'validate_certs': True, + 'client_id': 'admin-cli', + 'token': "alongtoken" + } + keycloak_header = get_token(module_params_token) + assert keycloak_header == { + 'Authorization': 'Bearer alongtoken', + 'Content-Type': 'application/json' + } + + +@pytest.fixture() +def mock_bad_json_returned(mocker): + token_response = { + 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token': create_wrapper('{"access_token":'), } + return mocker.patch( + 'ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak.open_url', + side_effect=build_mocked_request(count(), token_response), + autospec=True + ) + + +def test_bad_json_returned(mock_bad_json_returned): + with pytest.raises(KeycloakError) as raised_error: + get_token(module_params_creds) + # cannot check all the message, different errors message for the value + # error in python 2.6, 2.7 and 3.*. + assert ( + 'API returned invalid JSON when trying to obtain access token from ' + 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token: ' + ) in str(raised_error.value) + + +def raise_401(url): + def _raise_401(): + raise HTTPError(url=url, code=401, msg='Unauthorized', hdrs='', fp=StringIO('')) + return _raise_401 + + +@pytest.fixture() +def mock_401_returned(mocker): + token_response = { + 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token': raise_401( + 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token'), + } + return mocker.patch( + 'ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak.open_url', + side_effect=build_mocked_request(count(), token_response), + autospec=True + ) + + +def test_error_returned(mock_401_returned): + with pytest.raises(KeycloakError) as raised_error: + get_token(module_params_creds) + assert str(raised_error.value) == ( + 'Could not obtain access token from http://keycloak.url' + '/auth/realms/master/protocol/openid-connect/token: ' + 'HTTP Error 401: Unauthorized' + ) + + +@pytest.fixture() +def mock_json_without_token_returned(mocker): + token_response = { + 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token': create_wrapper('{"not_token": "It is not a token"}'), } + return mocker.patch( + 'ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak.open_url', + side_effect=build_mocked_request(count(), token_response), + autospec=True + ) + + +def test_json_without_token_returned(mock_json_without_token_returned): + with pytest.raises(KeycloakError) as raised_error: + get_token(module_params_creds) + assert str(raised_error.value) == ( + 'Could not obtain access token from http://keycloak.url' + '/auth/realms/master/protocol/openid-connect/token' + ) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py new file mode 100644 index 000000000..dc0f8d3f9 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/identity/keycloak/test_keycloak_module_utils.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import unittest + +from ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak import is_struct_included + + +class KeycloakIsStructIncludedTestCase(unittest.TestCase): + dict1 = dict( + test1='test1', + test2=dict( + test1='test1', + test2='test2' + ), + test3=['test1', dict(test='test1', test2='test2')] + ) + dict2 = dict( + test1='test1', + test2=dict( + test1='test1', + test2='test2', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test2'), 'test3'], + test4='test4' + ) + dict3 = dict( + test1='test1', + test2=dict( + test1='test1', + test2='test23', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test23'), 'test3'], + test4='test4' + ) + + dict5 = dict( + test1='test1', + test2=dict( + test1=True, + test2='test23', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test23'), 'test3'], + test4='test4' + ) + + dict6 = dict( + test1='test1', + test2=dict( + test1='true', + test2='test23', + test3='test3' + ), + test3=['test1', dict(test='test1', test2='test23'), 'test3'], + test4='test4' + ) + dict7 = [ + { + 'roles': ['view-clients', 'view-identity-providers', 'view-users', 'query-realms', 'manage-users'], + 'clientid': 'master-realm' + }, + { + 'roles': ['manage-account', 'view-profile', 'manage-account-links'], + 'clientid': 'account' + } + ] + dict8 = [ + { + 'roles': ['view-clients', 'query-realms', 'view-users'], + 'clientid': 'master-realm' + }, + { + 'roles': ['manage-account-links', 'view-profile', 'manage-account'], + 'clientid': 'account' + } + ] + + def test_trivial(self): + self.assertTrue(is_struct_included(self.dict1, self.dict1)) + + def test_equals_with_dict2_bigger_than_dict1(self): + self.assertTrue(is_struct_included(self.dict1, self.dict2)) + + def test_not_equals_with_dict2_bigger_than_dict1(self): + self.assertFalse(is_struct_included(self.dict2, self.dict1)) + + def test_not_equals_with_dict1_different_than_dict3(self): + self.assertFalse(is_struct_included(self.dict1, self.dict3)) + + def test_equals_with_dict5_contain_bool_and_dict6_contain_true_string(self): + self.assertFalse(is_struct_included(self.dict5, self.dict6)) + self.assertFalse(is_struct_included(self.dict6, self.dict5)) + + def test_not_equals_dict7_dict8_compare_dict7_with_list_bigger_than_dict8_but_reverse_equals(self): + self.assertFalse(is_struct_included(self.dict7, self.dict8)) + self.assertTrue(is_struct_included(self.dict8, self.dict7)) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/net_tools/pritunl/test_api.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/net_tools/pritunl/test_api.py new file mode 100644 index 000000000..6ddc827a1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/net_tools/pritunl/test_api.py @@ -0,0 +1,632 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Florian Dambrine +# 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 + +import json + +import pytest +from ansible.module_utils.common.dict_transformations import dict_merge +from ansible.module_utils.six import iteritems +from ansible_collections.community.general.plugins.module_utils.net_tools.pritunl import ( + api, +) +from mock import MagicMock + +__metaclass__ = type + + +# Pritunl Mocks + +PRITUNL_ORGS = [ + { + "auth_api": False, + "name": "Foo", + "auth_token": None, + "user_count": 0, + "auth_secret": None, + "id": "csftwlu6uhralzi2dpmhekz3", + }, + { + "auth_api": False, + "name": "GumGum", + "auth_token": None, + "user_count": 3, + "auth_secret": None, + "id": "58070daee63f3b2e6e472c36", + }, + { + "auth_api": False, + "name": "Bar", + "auth_token": None, + "user_count": 0, + "auth_secret": None, + "id": "v1sncsxxybnsylc8gpqg85pg", + }, +] + +NEW_PRITUNL_ORG = { + "auth_api": False, + "name": "NewOrg", + "auth_token": None, + "user_count": 0, + "auth_secret": None, + "id": "604a140ae63f3b36bc34c7bd", +} + +PRITUNL_USERS = [ + { + "auth_type": "google", + "dns_servers": None, + "pin": True, + "dns_suffix": None, + "servers": [ + { + "status": False, + "platform": None, + "server_id": "580711322bb66c1d59b9568f", + "virt_address6": "fd00:c0a8: 9700: 0: 192: 168: 101: 27", + "virt_address": "192.168.101.27", + "name": "vpn-A", + "real_address": None, + "connected_since": None, + "id": "580711322bb66c1d59b9568f", + "device_name": None, + }, + { + "status": False, + "platform": None, + "server_id": "5dad2cc6e63f3b3f4a6dfea5", + "virt_address6": "fd00:c0a8:f200: 0: 192: 168: 201: 37", + "virt_address": "192.168.201.37", + "name": "vpn-B", + "real_address": None, + "connected_since": None, + "id": "5dad2cc6e63f3b3f4a6dfea5", + "device_name": None, + }, + ], + "disabled": False, + "network_links": [], + "port_forwarding": [], + "id": "58070dafe63f3b2e6e472c3b", + "organization_name": "GumGum", + "type": "server", + "email": "bot@company.com", + "status": True, + "dns_mapping": None, + "otp_secret": "123456789ABCDEFG", + "client_to_client": False, + "sso": "google", + "bypass_secondary": False, + "groups": ["admin", "multiregion"], + "audit": False, + "name": "bot", + "gravatar": True, + "otp_auth": True, + "organization": "58070daee63f3b2e6e472c36", + }, + { + "auth_type": "google", + "dns_servers": None, + "pin": True, + "dns_suffix": None, + "servers": [ + { + "status": False, + "platform": None, + "server_id": "580711322bb66c1d59b9568f", + "virt_address6": "fd00:c0a8: 9700: 0: 192: 168: 101: 27", + "virt_address": "192.168.101.27", + "name": "vpn-A", + "real_address": None, + "connected_since": None, + "id": "580711322bb66c1d59b9568f", + "device_name": None, + }, + { + "status": False, + "platform": None, + "server_id": "5dad2cc6e63f3b3f4a6dfea5", + "virt_address6": "fd00:c0a8:f200: 0: 192: 168: 201: 37", + "virt_address": "192.168.201.37", + "name": "vpn-B", + "real_address": None, + "connected_since": None, + "id": "5dad2cc6e63f3b3f4a6dfea5", + "device_name": None, + }, + ], + "disabled": False, + "network_links": [], + "port_forwarding": [], + "id": "58070dafe63f3b2e6e472c3b", + "organization_name": "GumGum", + "type": "client", + "email": "florian@company.com", + "status": True, + "dns_mapping": None, + "otp_secret": "123456789ABCDEFG", + "client_to_client": False, + "sso": "google", + "bypass_secondary": False, + "groups": ["web", "database"], + "audit": False, + "name": "florian", + "gravatar": True, + "otp_auth": True, + "organization": "58070daee63f3b2e6e472c36", + }, + { + "auth_type": "google", + "dns_servers": None, + "pin": True, + "dns_suffix": None, + "servers": [ + { + "status": False, + "platform": None, + "server_id": "580711322bb66c1d59b9568f", + "virt_address6": "fd00:c0a8: 9700: 0: 192: 168: 101: 27", + "virt_address": "192.168.101.27", + "name": "vpn-A", + "real_address": None, + "connected_since": None, + "id": "580711322bb66c1d59b9568f", + "device_name": None, + }, + { + "status": False, + "platform": None, + "server_id": "5dad2cc6e63f3b3f4a6dfea5", + "virt_address6": "fd00:c0a8:f200: 0: 192: 168: 201: 37", + "virt_address": "192.168.201.37", + "name": "vpn-B", + "real_address": None, + "connected_since": None, + "id": "5dad2cc6e63f3b3f4a6dfea5", + "device_name": None, + }, + ], + "disabled": False, + "network_links": [], + "port_forwarding": [], + "id": "58070dafe63f3b2e6e472c3b", + "organization_name": "GumGum", + "type": "server", + "email": "ops@company.com", + "status": True, + "dns_mapping": None, + "otp_secret": "123456789ABCDEFG", + "client_to_client": False, + "sso": "google", + "bypass_secondary": False, + "groups": ["web", "database"], + "audit": False, + "name": "ops", + "gravatar": True, + "otp_auth": True, + "organization": "58070daee63f3b2e6e472c36", + }, +] + +NEW_PRITUNL_USER = { + "auth_type": "local", + "disabled": False, + "dns_servers": None, + "otp_secret": "6M4UWP2BCJBSYZAT", + "name": "alice", + "pin": False, + "dns_suffix": None, + "client_to_client": False, + "email": "alice@company.com", + "organization_name": "GumGum", + "bypass_secondary": False, + "groups": ["a", "b"], + "organization": "58070daee63f3b2e6e472c36", + "port_forwarding": [], + "type": "client", + "id": "590add71e63f3b72d8bb951a", +} + +NEW_PRITUNL_USER_UPDATED = dict_merge( + NEW_PRITUNL_USER, + { + "disabled": True, + "name": "bob", + "email": "bob@company.com", + "groups": ["c", "d"], + }, +) + + +class PritunlEmptyOrganizationMock(MagicMock): + """Pritunl API Mock for organization GET API calls.""" + + def getcode(self): + return 200 + + def read(self): + return json.dumps([]) + + +class PritunlListOrganizationMock(MagicMock): + """Pritunl API Mock for organization GET API calls.""" + + def getcode(self): + return 200 + + def read(self): + return json.dumps(PRITUNL_ORGS) + + +class PritunlListUserMock(MagicMock): + """Pritunl API Mock for user GET API calls.""" + + def getcode(self): + return 200 + + def read(self): + return json.dumps(PRITUNL_USERS) + + +class PritunlErrorMock(MagicMock): + """Pritunl API Mock for API call failures.""" + + def getcode(self): + return 500 + + def read(self): + return "{}" + + +class PritunlPostOrganizationMock(MagicMock): + def getcode(self): + return 200 + + def read(self): + return json.dumps(NEW_PRITUNL_ORG) + + +class PritunlListOrganizationAfterPostMock(MagicMock): + def getcode(self): + return 200 + + def read(self): + return json.dumps(PRITUNL_ORGS + [NEW_PRITUNL_ORG]) + + +class PritunlPostUserMock(MagicMock): + """Pritunl API Mock for POST API calls.""" + + def getcode(self): + return 200 + + def read(self): + return json.dumps([NEW_PRITUNL_USER]) + + +class PritunlPutUserMock(MagicMock): + """Pritunl API Mock for PUT API calls.""" + + def getcode(self): + return 200 + + def read(self): + return json.dumps(NEW_PRITUNL_USER_UPDATED) + + +class PritunlDeleteOrganizationMock(MagicMock): + """Pritunl API Mock for DELETE API calls.""" + + def getcode(self): + return 200 + + def read(self): + return "{}" + + +class PritunlDeleteUserMock(MagicMock): + """Pritunl API Mock for DELETE API calls.""" + + def getcode(self): + return 200 + + def read(self): + return "{}" + + +# Ansible Module Mock and Pytest mock fixtures + + +class ModuleFailException(Exception): + def __init__(self, msg, **kwargs): + super(ModuleFailException, self).__init__(msg) + self.fail_msg = msg + self.fail_kwargs = kwargs + + +@pytest.fixture +def pritunl_settings(): + return { + "api_token": "token", + "api_secret": "secret", + "base_url": "https://pritunl.domain.com", + "validate_certs": True, + } + + +@pytest.fixture +def pritunl_organization_data(): + return { + "name": NEW_PRITUNL_ORG["name"], + } + + +@pytest.fixture +def pritunl_user_data(): + return { + "name": NEW_PRITUNL_USER["name"], + "email": NEW_PRITUNL_USER["email"], + "groups": NEW_PRITUNL_USER["groups"], + "disabled": NEW_PRITUNL_USER["disabled"], + "type": NEW_PRITUNL_USER["type"], + } + + +@pytest.fixture +def get_pritunl_organization_mock(): + return PritunlListOrganizationMock() + + +@pytest.fixture +def get_pritunl_user_mock(): + return PritunlListUserMock() + + +@pytest.fixture +def get_pritunl_error_mock(): + return PritunlErrorMock() + + +@pytest.fixture +def post_pritunl_organization_mock(): + return PritunlPostOrganizationMock() + + +@pytest.fixture +def post_pritunl_user_mock(): + return PritunlPostUserMock() + + +@pytest.fixture +def put_pritunl_user_mock(): + return PritunlPutUserMock() + + +@pytest.fixture +def delete_pritunl_organization_mock(): + return PritunlDeleteOrganizationMock() + + +@pytest.fixture +def delete_pritunl_user_mock(): + return PritunlDeleteUserMock() + + +class TestPritunlApi: + """ + Test class to validate CRUD operations on Pritunl. + """ + + # Test for GET / list operation on Pritunl API + @pytest.mark.parametrize( + "org_id,org_user_count", + [ + ("58070daee63f3b2e6e472c36", 3), + ("v1sncsxxybnsylc8gpqg85pg", 0), + ], + ) + def test_list_all_pritunl_organization( + self, + pritunl_settings, + get_pritunl_organization_mock, + org_id, + org_user_count, + ): + api._get_pritunl_organizations = get_pritunl_organization_mock() + + response = api.list_pritunl_organizations(**pritunl_settings) + + assert len(response) == 3 + + for org in response: + if org["id"] == org_id: + org["user_count"] == org_user_count + + @pytest.mark.parametrize( + "org_filters,org_expected", + [ + ({"id": "58070daee63f3b2e6e472c36"}, "GumGum"), + ({"name": "GumGum"}, "GumGum"), + ], + ) + def test_list_filtered_pritunl_organization( + self, + pritunl_settings, + get_pritunl_organization_mock, + org_filters, + org_expected, + ): + api._get_pritunl_organizations = get_pritunl_organization_mock() + + response = api.list_pritunl_organizations( + **dict_merge(pritunl_settings, {"filters": org_filters}) + ) + + assert len(response) == 1 + assert response[0]["name"] == org_expected + + @pytest.mark.parametrize( + "org_id,org_user_count", + [("58070daee63f3b2e6e472c36", 3)], + ) + def test_list_all_pritunl_user( + self, pritunl_settings, get_pritunl_user_mock, org_id, org_user_count + ): + api._get_pritunl_users = get_pritunl_user_mock() + + response = api.list_pritunl_users( + **dict_merge(pritunl_settings, {"organization_id": org_id}) + ) + + assert len(response) == org_user_count + + @pytest.mark.parametrize( + "org_id,user_filters,user_expected", + [ + ("58070daee63f3b2e6e472c36", {"email": "bot@company.com"}, "bot"), + ("58070daee63f3b2e6e472c36", {"name": "florian"}, "florian"), + ], + ) + def test_list_filtered_pritunl_user( + self, + pritunl_settings, + get_pritunl_user_mock, + org_id, + user_filters, + user_expected, + ): + api._get_pritunl_users = get_pritunl_user_mock() + + response = api.list_pritunl_users( + **dict_merge( + pritunl_settings, {"organization_id": org_id, "filters": user_filters} + ) + ) + + assert len(response) > 0 + + for user in response: + assert user["organization"] == org_id + assert user["name"] == user_expected + + # Test for POST operation on Pritunl API + def test_add_pritunl_organization( + self, + pritunl_settings, + pritunl_organization_data, + post_pritunl_organization_mock, + ): + api._post_pritunl_organization = post_pritunl_organization_mock() + + create_response = api.post_pritunl_organization( + **dict_merge( + pritunl_settings, + {"organization_name": pritunl_organization_data["name"]}, + ) + ) + + # Ensure provided settings match with the ones returned by Pritunl + for k, v in iteritems(pritunl_organization_data): + assert create_response[k] == v + + @pytest.mark.parametrize("org_id", [("58070daee63f3b2e6e472c36")]) + def test_add_and_update_pritunl_user( + self, + pritunl_settings, + pritunl_user_data, + post_pritunl_user_mock, + put_pritunl_user_mock, + org_id, + ): + api._post_pritunl_user = post_pritunl_user_mock() + api._put_pritunl_user = put_pritunl_user_mock() + + create_response = api.post_pritunl_user( + **dict_merge( + pritunl_settings, + { + "organization_id": org_id, + "user_data": pritunl_user_data, + }, + ) + ) + + # Ensure provided settings match with the ones returned by Pritunl + for k, v in iteritems(pritunl_user_data): + assert create_response[k] == v + + # Update the newly created user to ensure only certain settings are changed + + user_updates = { + "name": "bob", + "email": "bob@company.com", + "disabled": True, + } + + update_response = api.post_pritunl_user( + **dict_merge( + pritunl_settings, + { + "organization_id": org_id, + "user_id": create_response["id"], + "user_data": dict_merge(pritunl_user_data, user_updates), + }, + ) + ) + + # Ensure only certain settings changed and the rest remained untouched. + for k, v in iteritems(update_response): + if k in update_response: + assert update_response[k] == v + else: + assert update_response[k] == create_response[k] + + # Test for DELETE operation on Pritunl API + + @pytest.mark.parametrize("org_id", [("58070daee63f3b2e6e472c36")]) + def test_delete_pritunl_organization( + self, pritunl_settings, org_id, delete_pritunl_organization_mock + ): + api._delete_pritunl_organization = delete_pritunl_organization_mock() + + response = api.delete_pritunl_organization( + **dict_merge( + pritunl_settings, + { + "organization_id": org_id, + }, + ) + ) + + assert response == {} + + @pytest.mark.parametrize( + "org_id,user_id", [("58070daee63f3b2e6e472c36", "590add71e63f3b72d8bb951a")] + ) + def test_delete_pritunl_user( + self, pritunl_settings, org_id, user_id, delete_pritunl_user_mock + ): + api._delete_pritunl_user = delete_pritunl_user_mock() + + response = api.delete_pritunl_user( + **dict_merge( + pritunl_settings, + { + "organization_id": org_id, + "user_id": user_id, + }, + ) + ) + + assert response == {} + + # Test API call errors + def test_pritunl_error(self, pritunl_settings, get_pritunl_error_mock): + api.pritunl_auth_request = get_pritunl_error_mock() + + with pytest.raises(api.PritunlException): + response = api.list_pritunl_organizations(**pritunl_settings) 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 new file mode 100644 index 000000000..7cec215a7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Alexei Znamensky +# 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 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 + + +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"]), +) +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, + ("dict",), + OrderedDict((('a', 1), ('b', 2))), + ["--a=1", "--b=2"] + ) +TC_FORMATS_IDS = sorted(TC_FORMATS.keys()) + + +@pytest.mark.parametrize('func, fmt_opt, value, expected', + (TC_FORMATS[tc] for tc in TC_FORMATS_IDS), + ids=TC_FORMATS_IDS) +def test_arg_format(func, fmt_opt, value, expected): + fmt_func = func(*fmt_opt) + actual = fmt_func(value, ctx_ignore_none=True) + print("formatted string = {0}".format(actual)) + assert actual == expected, "actual = {0}".format(actual) + + +TC_RUNNER = dict( + # SAMPLE: This shows all possible elements of a test case. It does not actually run. + # + # testcase_name=( + # # input + # dict( + # args_bundle = dict( + # param1=dict( + # type="int", + # value=11, + # fmt_func=fmt.as_opt_eq_val, + # fmt_arg="--answer", + # ), + # param2=dict( + # fmt_func=fmt.as_bool, + # fmt_arg="--bb-here", + # ) + # ), + # runner_init_args = dict( + # command="testing", + # default_args_order=(), + # check_rc=False, + # force_lang="C", + # path_prefix=None, + # environ_update=None, + # ), + # runner_ctx_args = dict( + # args_order=['aa', 'bb'], + # output_process=None, + # ignore_value_none=True, + # ), + # ), + # # command execution + # dict( + # runner_ctx_run_args = dict(bb=True), + # rc = 0, + # out = "", + # err = "", + # ), + # # expected + # dict( + # results=(), + # run_info=dict( + # cmd=['/mock/bin/testing', '--answer=11', '--bb-here'], + # environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + # ), + # exc=None, + # ), + # ), + # + 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"), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', '--bb-here'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + 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"), + ), + runner_init_args=dict(default_args_order=['bb', 'aa']), + runner_ctx_args=dict(), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--bb-here', '--answer=11'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('bb', 'aa'), + ), + ), + ), + 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"), + ), + runner_init_args=dict(default_args_order=['bb', 'aa']), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', '--bb-here'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + 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"), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb', 'aa']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', '--bb-here', '--answer=11'], + ), + ), + ), + 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"), + ), + runner_init_args=dict(default_args_order=['bb', 'aa']), + runner_ctx_args=dict( + args_order=['aa', 'bb'], + output_process=lambda rc, out, err: '-/-'.join([str(rc), out, err]) + ), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="ni", err="nu"), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', '--bb-here'], + ), + results="0-/-ni-/-nu" + ), + ), + 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"), + ), + runner_init_args=dict(default_args_order=['bb', 'aa']), + runner_ctx_args=dict( + args_order=['aa', 'bb'], + ignore_value_none=True, # default + ), + ), + dict(runner_ctx_run_args=dict(bb=None), rc=0, out="ni", err="nu"), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=49'], + ), + ), + ), + 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"), + ), + runner_init_args=dict(default_args_order=['bb', 'aa']), + runner_ctx_args=dict( + args_order=['aa', 'bb'], + ignore_value_none=False, + ), + ), + dict(runner_ctx_run_args=dict(aa=None, bb=True), rc=0, out="ni", err="nu"), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=None', '--bb-here'], + ), + ), + ), + 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"]), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', 'fixed', 'args'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + 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}), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb="v2"), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11', '222'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + 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}), + ), + runner_init_args=dict(), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb="v123456789"), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/testing', '--answer=11'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), +) +TC_RUNNER_IDS = sorted(TC_RUNNER.keys()) + + +@pytest.mark.parametrize('runner_input, cmd_execution, expected', + (TC_RUNNER[tc] for tc in TC_RUNNER_IDS), + ids=TC_RUNNER_IDS) +def test_runner_context(runner_input, cmd_execution, expected): + arg_spec = {} + params = {} + arg_formats = {} + for k, v in runner_input['args_bundle'].items(): + try: + arg_spec[k] = {'type': v['type']} + except KeyError: + pass + try: + params[k] = v['value'] + except KeyError: + pass + try: + arg_formats[k] = v['fmt_func'](v['fmt_arg']) + except KeyError: + pass + + orig_results = tuple(cmd_execution[x] for x in ('rc', 'out', 'err')) + + print("arg_spec={0}\nparams={1}\narg_formats={2}\n".format( + arg_spec, + params, + arg_formats, + )) + + module = MagicMock() + type(module).argument_spec = PropertyMock(return_value=arg_spec) + type(module).params = PropertyMock(return_value=params) + module.get_bin_path.return_value = '/mock/bin/testing' + module.run_command.return_value = orig_results + + runner = CmdRunner( + module=module, + command="testing", + arg_formats=arg_formats, + **runner_input['runner_init_args'] + ) + + def _assert_run_info(actual, expected): + reduced = dict((k, actual[k]) for k in expected.keys()) + assert reduced == expected, "{0}".format(reduced) + + def _assert_run(runner_input, cmd_execution, expected, ctx, results): + _assert_run_info(ctx.run_info, expected['run_info']) + assert results == expected.get('results', orig_results) + + exc = expected.get("exc") + if exc: + with pytest.raises(exc): + with runner.context(**runner_input['runner_ctx_args']) as ctx: + results = ctx.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx, results) + + with pytest.raises(exc): + with runner(**runner_input['runner_ctx_args']) as ctx2: + results2 = ctx2.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx2, results2) + + else: + with runner.context(**runner_input['runner_ctx_args']) as ctx: + results = ctx.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx, results) + + with runner(**runner_input['runner_ctx_args']) as ctx2: + results2 = ctx2.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(runner_input, cmd_execution, expected, ctx2, results2) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_csv.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_csv.py new file mode 100644 index 000000000..8b83908e7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_csv.py @@ -0,0 +1,166 @@ +# -*- 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 pytest + +from ansible_collections.community.general.plugins.module_utils import csv + + +VALID_CSV = [ + ( + 'excel', + {}, + None, + "id,name,role\n1,foo,bar\n2,bar,baz", + [ + { + "id": "1", + "name": "foo", + "role": "bar", + }, + { + "id": "2", + "name": "bar", + "role": "baz", + }, + ] + ), + ( + 'excel', + {"skipinitialspace": True}, + None, + "id,name,role\n1, foo, bar\n2, bar, baz", + [ + { + "id": "1", + "name": "foo", + "role": "bar", + }, + { + "id": "2", + "name": "bar", + "role": "baz", + }, + ] + ), + ( + 'excel', + {"delimiter": '|'}, + None, + "id|name|role\n1|foo|bar\n2|bar|baz", + [ + { + "id": "1", + "name": "foo", + "role": "bar", + }, + { + "id": "2", + "name": "bar", + "role": "baz", + }, + ] + ), + ( + 'unix', + {}, + None, + "id,name,role\n1,foo,bar\n2,bar,baz", + [ + { + "id": "1", + "name": "foo", + "role": "bar", + }, + { + "id": "2", + "name": "bar", + "role": "baz", + }, + ] + ), + ( + 'excel', + {}, + ['id', 'name', 'role'], + "1,foo,bar\n2,bar,baz", + [ + { + "id": "1", + "name": "foo", + "role": "bar", + }, + { + "id": "2", + "name": "bar", + "role": "baz", + }, + ] + ), +] + +INVALID_CSV = [ + ( + 'excel', + {'strict': True}, + None, + 'id,name,role\n1,"f"oo",bar\n2,bar,baz', + ), +] + +INVALID_DIALECT = [ + ( + 'invalid', + {}, + None, + "id,name,role\n1,foo,bar\n2,bar,baz", + ), +] + + +@pytest.mark.parametrize("dialect,dialect_params,fieldnames,data,expected", VALID_CSV) +def test_valid_csv(data, dialect, dialect_params, fieldnames, expected): + dialect = csv.initialize_dialect(dialect, **dialect_params) + reader = csv.read_csv(data, dialect, fieldnames) + result = True + + for idx, row in enumerate(reader): + for k, v in row.items(): + if expected[idx][k] != v: + result = False + break + + assert result + + +@pytest.mark.parametrize("dialect,dialect_params,fieldnames,data", INVALID_CSV) +def test_invalid_csv(data, dialect, dialect_params, fieldnames): + dialect = csv.initialize_dialect(dialect, **dialect_params) + reader = csv.read_csv(data, dialect, fieldnames) + result = False + + try: + for row in reader: + continue + except csv.CSVError: + result = True + + assert result + + +@pytest.mark.parametrize("dialect,dialect_params,fieldnames,data", INVALID_DIALECT) +def test_invalid_dialect(data, dialect, dialect_params, fieldnames): + result = False + + try: + dialect = csv.initialize_dialect(dialect, **dialect_params) + except csv.DialectNotAvailableError: + result = True + + assert result diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_database.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_database.py new file mode 100644 index 000000000..c76671202 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_database.py @@ -0,0 +1,143 @@ +# 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 pytest + +from ansible_collections.community.general.plugins.module_utils.database import ( + is_input_dangerous, + pg_quote_identifier, + SQLParseError, +) + +# These are all valid strings +# The results are based on interpreting the identifier as a table name +VALID = { + # User quoted + '"public.table"': '"public.table"', + '"public"."table"': '"public"."table"', + '"schema test"."table test"': '"schema test"."table test"', + + # We quote part + 'public.table': '"public"."table"', + '"public".table': '"public"."table"', + 'public."table"': '"public"."table"', + 'schema test.table test': '"schema test"."table test"', + '"schema test".table test': '"schema test"."table test"', + 'schema test."table test"': '"schema test"."table test"', + + # Embedded double quotes + 'table "test"': '"table ""test"""', + 'public."table ""test"""': '"public"."table ""test"""', + 'public.table "test"': '"public"."table ""test"""', + 'schema "test".table': '"schema ""test"""."table"', + '"schema ""test""".table': '"schema ""test"""."table"', + '"""wat"""."""test"""': '"""wat"""."""test"""', + # Sigh, handle these as well: + '"no end quote': '"""no end quote"', + 'schema."table': '"schema"."""table"', + '"schema.table': '"""schema"."table"', + 'schema."table.something': '"schema"."""table"."something"', + + # Embedded dots + '"schema.test"."table.test"': '"schema.test"."table.test"', + '"schema.".table': '"schema."."table"', + '"schema."."table"': '"schema."."table"', + 'schema.".table"': '"schema".".table"', + '"schema".".table"': '"schema".".table"', + '"schema.".".table"': '"schema.".".table"', + # These are valid but maybe not what the user intended + '."table"': '".""table"""', + 'table.': '"table."', +} + +INVALID = { + ('test.too.many.dots', 'table'): 'PostgreSQL does not support table with more than 3 dots', + ('"test.too".many.dots', 'database'): 'PostgreSQL does not support database with more than 1 dots', + ('test.too."many.dots"', 'database'): 'PostgreSQL does not support database with more than 1 dots', + ('"test"."too"."many"."dots"', 'database'): "PostgreSQL does not support database with more than 1 dots", + ('"test"."too"."many"."dots"', 'schema'): "PostgreSQL does not support schema with more than 2 dots", + ('"test"."too"."many"."dots"', 'table'): "PostgreSQL does not support table with more than 3 dots", + ('"test"."too"."many"."dots"."for"."column"', 'column'): "PostgreSQL does not support column with more than 4 dots", + ('"table "invalid" double quote"', 'table'): 'User escaped identifiers must escape extra quotes', + ('"schema "invalid"""."table "invalid"', 'table'): 'User escaped identifiers must escape extra quotes', + ('"schema."table"', 'table'): 'User escaped identifiers must escape extra quotes', + ('"schema".', 'table'): 'Identifier name unspecified or unquoted trailing dot', +} + +HOW_MANY_DOTS = ( + ('role', 'role', '"role"', + 'PostgreSQL does not support role with more than 1 dots'), + ('db', 'database', '"db"', + 'PostgreSQL does not support database with more than 1 dots'), + ('db.schema', 'schema', '"db"."schema"', + 'PostgreSQL does not support schema with more than 2 dots'), + ('db.schema.table', 'table', '"db"."schema"."table"', + 'PostgreSQL does not support table with more than 3 dots'), + ('db.schema.table.column', 'column', '"db"."schema"."table"."column"', + 'PostgreSQL does not support column with more than 4 dots'), +) + +VALID_QUOTES = ((test, VALID[test]) for test in sorted(VALID)) +INVALID_QUOTES = ((test[0], test[1], INVALID[test]) for test in sorted(INVALID)) + +IS_STRINGS_DANGEROUS = ( + (u'', False), + (u' ', False), + (u'alternative database', False), + (u'backup of TRUNCATED table', False), + (u'bob.dropper', False), + (u'd\'artagnan', False), + (u'user_with_select_update_truncate_right', False), + (u';DROP DATABASE fluffy_pets_photos', True), + (u';drop DATABASE fluffy_pets_photos', True), + (u'; TRUNCATE TABLE his_valuable_table', True), + (u'; truncate TABLE his_valuable_table', True), + (u'\'--', True), + (u'"--', True), + (u'\' union select username, password from admin_credentials', True), + (u'\' UNION SELECT username, password from admin_credentials', True), + (u'\' intersect select', True), + (u'\' INTERSECT select', True), + (u'\' except select', True), + (u'\' EXCEPT select', True), + (u';ALTER TABLE prices', True), + (u';alter table prices', True), + (u"; UPDATE products SET price = '0'", True), + (u";update products SET price = '0'", True), + (u"; DELETE FROM products", True), + (u"; delete FROM products", True), + (u"; SELECT * FROM products", True), + (u" ; select * from products", True), +) + + +@pytest.mark.parametrize("identifier, quoted_identifier", VALID_QUOTES) +def test_valid_quotes(identifier, quoted_identifier): + assert pg_quote_identifier(identifier, 'table') == quoted_identifier + + +@pytest.mark.parametrize("identifier, id_type, msg", INVALID_QUOTES) +def test_invalid_quotes(identifier, id_type, msg): + with pytest.raises(SQLParseError) as ex: + pg_quote_identifier(identifier, id_type) + + ex.match(msg) + + +@pytest.mark.parametrize("identifier, id_type, quoted_identifier, msg", HOW_MANY_DOTS) +def test_how_many_dots(identifier, id_type, quoted_identifier, msg): + assert pg_quote_identifier(identifier, id_type) == quoted_identifier + + with pytest.raises(SQLParseError) as ex: + pg_quote_identifier('%s.more' % identifier, id_type) + + ex.match(msg) + + +@pytest.mark.parametrize("string, result", IS_STRINGS_DANGEROUS) +def test_is_input_dangerous(string, result): + assert is_input_dangerous(string) == result diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_known_hosts.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_known_hosts.py new file mode 100644 index 000000000..25e76b66f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_known_hosts.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# (c) 2015, Michael Scherer +# Copyright (c) 2017 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 pytest + +from ansible_collections.community.general.plugins.module_utils import known_hosts + + +URLS = { + 'ssh://one.example.org/example.git': { + 'is_ssh_url': True, + 'get_fqdn': 'one.example.org', + 'add_host_key_cmd': " -t rsa one.example.org", + 'port': None, + }, + 'ssh+git://two.example.org/example.git': { + 'is_ssh_url': True, + 'get_fqdn': 'two.example.org', + 'add_host_key_cmd': " -t rsa two.example.org", + 'port': None, + }, + 'rsync://three.example.org/user/example.git': { + 'is_ssh_url': False, + 'get_fqdn': 'three.example.org', + 'add_host_key_cmd': None, # not called for non-ssh urls + 'port': None, + }, + 'git@four.example.org:user/example.git': { + 'is_ssh_url': True, + 'get_fqdn': 'four.example.org', + 'add_host_key_cmd': " -t rsa four.example.org", + 'port': None, + }, + 'git+ssh://five.example.org/example.git': { + 'is_ssh_url': True, + 'get_fqdn': 'five.example.org', + 'add_host_key_cmd': " -t rsa five.example.org", + 'port': None, + }, + 'ssh://six.example.org:21/example.org': { + # ssh on FTP Port? + 'is_ssh_url': True, + 'get_fqdn': 'six.example.org', + 'add_host_key_cmd': " -t rsa -p 21 six.example.org", + 'port': '21', + }, + 'ssh://[2001:DB8::abcd:abcd]/example.git': { + 'is_ssh_url': True, + 'get_fqdn': '[2001:DB8::abcd:abcd]', + 'add_host_key_cmd': " -t rsa [2001:DB8::abcd:abcd]", + 'port': None, + }, + 'ssh://[2001:DB8::abcd:abcd]:22/example.git': { + 'is_ssh_url': True, + 'get_fqdn': '[2001:DB8::abcd:abcd]', + 'add_host_key_cmd': " -t rsa -p 22 [2001:DB8::abcd:abcd]", + 'port': '22', + }, + 'username@[2001:DB8::abcd:abcd]/example.git': { + 'is_ssh_url': True, + 'get_fqdn': '[2001:DB8::abcd:abcd]', + 'add_host_key_cmd': " -t rsa [2001:DB8::abcd:abcd]", + 'port': None, + }, + 'username@[2001:DB8::abcd:abcd]:path/example.git': { + 'is_ssh_url': True, + 'get_fqdn': '[2001:DB8::abcd:abcd]', + 'add_host_key_cmd': " -t rsa [2001:DB8::abcd:abcd]", + 'port': None, + }, + 'ssh://internal.git.server:7999/repos/repo.git': { + 'is_ssh_url': True, + 'get_fqdn': 'internal.git.server', + 'add_host_key_cmd': " -t rsa -p 7999 internal.git.server", + 'port': '7999', + }, +} + + +@pytest.mark.parametrize('url, is_ssh_url', ((k, URLS[k]['is_ssh_url']) for k in sorted(URLS))) +def test_is_ssh_url(url, is_ssh_url): + assert known_hosts.is_ssh_url(url) == is_ssh_url + + +@pytest.mark.parametrize('url, fqdn, port', ((k, URLS[k]['get_fqdn'], URLS[k]['port']) for k in sorted(URLS))) +def test_get_fqdn_and_port(url, fqdn, port): + assert known_hosts.get_fqdn_and_port(url) == (fqdn, port) + + +@pytest.mark.parametrize('fqdn, port, add_host_key_cmd, stdin', + ((URLS[k]['get_fqdn'], URLS[k]['port'], URLS[k]['add_host_key_cmd'], {}) + for k in sorted(URLS) if URLS[k]['is_ssh_url']), + indirect=['stdin']) +def test_add_host_key(am, mocker, fqdn, port, add_host_key_cmd): + get_bin_path = mocker.MagicMock() + get_bin_path.return_value = keyscan_cmd = "/custom/path/ssh-keyscan" + am.get_bin_path = get_bin_path + + run_command = mocker.MagicMock() + run_command.return_value = (0, "Needs output, otherwise thinks ssh-keyscan timed out'", "") + am.run_command = run_command + + append_to_file = mocker.MagicMock() + append_to_file.return_value = (None,) + am.append_to_file = append_to_file + + mocker.patch('os.path.isdir', return_value=True) + mocker.patch('os.path.exists', return_value=True) + + known_hosts.add_host_key(am, fqdn, port=port) + run_command.assert_called_with(keyscan_cmd + add_host_key_cmd) 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 new file mode 100644 index 000000000..3d8a4b654 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +# (c) 2020, Alexei Znamensky +# Copyright (c) 2020 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 pytest + +from ansible_collections.community.general.plugins.module_utils.module_helper import ( + ArgFormat, 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: + import potatoes_that_will_never_be_there # noqa: F401, pylint: disable=unused-import + print("POTATOES: ctx.text={0}".format(ctx.text)) + assert ctx.text == "Potatoes must be installed" + assert not ctx.has_it + + ctx = DependencyCtxMgr("POTATOES2") + with ctx: + import potatoes_that_will_never_be_there_again # noqa: F401, pylint: disable=unused-import + assert not ctx.has_it + print("POTATOES2: ctx.text={0}".format(ctx.text)) + assert ctx.text.startswith("No module named") + assert "potatoes_that_will_never_be_there_again" in ctx.text + + ctx = DependencyCtxMgr("TYPING") + with ctx: + import sys # noqa: F401, pylint: disable=unused-import + assert ctx.has_it + + +def test_variable_meta(): + meta = VarMeta() + assert meta.output is True + assert meta.diff is False + assert meta.value is None + meta.set_value("abc") + assert meta.initial_value == "abc" + assert meta.value == "abc" + assert meta.diff_result is None + meta.set_value("def") + assert meta.initial_value == "abc" + assert meta.value == "def" + assert meta.diff_result is None + + +def test_variable_meta_diff(): + meta = VarMeta(diff=True) + assert meta.output is True + assert meta.diff is True + assert meta.value is None + meta.set_value("abc") + assert meta.initial_value == "abc" + assert meta.value == "abc" + assert meta.diff_result is None + meta.set_value("def") + assert meta.initial_value == "abc" + assert meta.value == "def" + assert meta.diff_result == {"before": "abc", "after": "def"} + meta.set_value("ghi") + assert meta.initial_value == "abc" + assert meta.value == "ghi" + assert meta.diff_result == {"before": "abc", "after": "ghi"} + + +def test_vardict(): + vd = VarDict() + vd.set('a', 123) + assert vd['a'] == 123 + assert vd.a == 123 + assert 'a' in vd._meta + assert vd.meta('a').output is True + assert vd.meta('a').diff is False + assert vd.meta('a').change is False + vd['b'] = 456 + assert vd.meta('b').output is True + assert vd.meta('b').diff is False + assert vd.meta('b').change is False + vd.set_meta('a', diff=True, change=True) + vd.set_meta('b', diff=True, output=False) + vd['c'] = 789 + assert vd.has_changed('c') is False + vd['a'] = 'new_a' + assert vd.has_changed('a') is True + vd['c'] = 'new_c' + assert vd.has_changed('c') is False + vd['b'] = 'new_b' + assert vd.has_changed('b') is False + assert vd.a == 'new_a' + assert vd.c == 'new_c' + assert vd.output() == {'a': 'new_a', 'c': 'new_c'} + assert vd.diff() == {'before': {'a': 123}, 'after': {'a': 'new_a'}}, "diff={0}".format(vd.diff()) + + +def test_variable_meta_change(): + vd = VarDict() + vd.set('a', 123, change=True) + vd.set('b', [4, 5, 6], change=True) + vd.set('c', {'m': 7, 'n': 8, 'o': 9}, change=True) + vd.set('d', {'a1': {'a11': 33, 'a12': 34}}, change=True) + + vd.a = 1234 + assert vd.has_changed('a') is True + vd.b.append(7) + assert vd.b == [4, 5, 6, 7] + assert vd.has_changed('b') + vd.c.update({'p': 10}) + assert vd.c == {'m': 7, 'n': 8, 'o': 9, 'p': 10} + assert vd.has_changed('c') + vd.d['a1'].update({'a13': 35}) + assert vd.d == {'a1': {'a11': 33, 'a12': 34, 'a13': 35}} + assert vd.has_changed('d') + + +class MockMH(object): + changed = None + + def _div(self, x, y): + return x / y + + func_none = cause_changes()(_div) + func_onsucc = cause_changes(on_success=True)(_div) + func_onfail = cause_changes(on_failure=True)(_div) + func_onboth = cause_changes(on_success=True, on_failure=True)(_div) + + +CAUSE_CHG_DECO_PARAMS = ['method', 'expect_exception', 'expect_changed'] +CAUSE_CHG_DECO = dict( + none_succ=dict(method='func_none', expect_exception=False, expect_changed=None), + none_fail=dict(method='func_none', expect_exception=True, expect_changed=None), + onsucc_succ=dict(method='func_onsucc', expect_exception=False, expect_changed=True), + onsucc_fail=dict(method='func_onsucc', expect_exception=True, expect_changed=None), + onfail_succ=dict(method='func_onfail', expect_exception=False, expect_changed=None), + onfail_fail=dict(method='func_onfail', expect_exception=True, expect_changed=True), + onboth_succ=dict(method='func_onboth', expect_exception=False, expect_changed=True), + onboth_fail=dict(method='func_onboth', expect_exception=True, expect_changed=True), +) +CAUSE_CHG_DECO_IDS = sorted(CAUSE_CHG_DECO.keys()) + + +@pytest.mark.parametrize(CAUSE_CHG_DECO_PARAMS, + [[CAUSE_CHG_DECO[tc][param] + for param in CAUSE_CHG_DECO_PARAMS] + for tc in CAUSE_CHG_DECO_IDS], + ids=CAUSE_CHG_DECO_IDS) +def test_cause_changes_deco(method, expect_exception, expect_changed): + mh = MockMH() + if expect_exception: + with pytest.raises(Exception): + getattr(mh, method)(1, 0) + else: + getattr(mh, method)(9, 3) + + assert mh.changed == expect_changed diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_ocapi_utils.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_ocapi_utils.py new file mode 100644 index 000000000..3c939b558 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_ocapi_utils.py @@ -0,0 +1,54 @@ +# -*- 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 os +import re +import shutil +import tempfile + +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.plugins.module_utils.ocapi_utils import OcapiUtils + + +class TestOcapiUtils(unittest.TestCase): + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.utils = OcapiUtils(creds={"user": "a_user", "pswd": "a_password"}, + base_uri="fakeUri", + proxy_slot_number=None, + timeout=30, + module=None) + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def test_prepare_multipart_firmware_upload(self): + # Generate a binary file and save it + filename = "fake_firmware.bin" + filepath = os.path.join(self.tempdir, filename) + file_contents = b'\x00\x01\x02\x03\x04' + with open(filepath, 'wb+') as f: + f.write(file_contents) + + # Call prepare_mutipart_firmware_upload + content_type, b_form_data = self.utils.prepare_multipart_firmware_upload(filepath) + + # Check the returned content-type + content_type_pattern = r"multipart/form-data; boundary=(.*)" + m = re.match(content_type_pattern, content_type) + self.assertIsNotNone(m) + + # Check the returned binary data + boundary = m.group(1) + expected_content_text = '--%s\r\n' % boundary + expected_content_text += 'Content-Disposition: form-data; name="FirmwareFile"; filename="%s"\r\n' % filename + expected_content_text += 'Content-Type: application/octet-stream\r\n\r\n' + expected_content_bytes = bytearray(expected_content_text, 'utf-8') + expected_content_bytes += file_contents + expected_content_bytes += bytearray('\r\n--%s--' % boundary, 'utf-8') + self.assertEqual(expected_content_bytes, b_form_data) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_onepassword.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_onepassword.py new file mode 100644 index 000000000..dbe391835 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_onepassword.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022 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 os + +import pytest + +from ansible_collections.community.general.plugins.module_utils.onepassword import OnePasswordConfig + + +@pytest.fixture +def os_expanduser(mocker): + def _os_expanduser(path): + return path.replace("~", "/home/testuser") + + mocker.patch("os.path.expanduser", side_effect=_os_expanduser) + + +@pytest.fixture +def exists(mocker): + def _exists(path): + if "op/" in path: + return True + + return os.path.exists(path) + + +def test_op_config(mocker, os_expanduser): + mocker.patch("os.path.exists", side_effect=[False, True]) + op_config = OnePasswordConfig() + + assert "/home/testuser/.config/op/config" == op_config.config_file_path + + +def test_op_no_config(mocker, os_expanduser): + mocker.patch("os.path.exists", return_value=False) + op_config = OnePasswordConfig() + + assert op_config.config_file_path is None diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_opennebula.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_opennebula.py new file mode 100644 index 000000000..dd6516984 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_opennebula.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Michal Opala +# 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 textwrap + +import pytest + +from ansible_collections.community.general.plugins.module_utils.opennebula import flatten, render + + +FLATTEN_VALID = [ + ( + [[[1]], [2], 3], + False, + [1, 2, 3] + ), + ( + [[[1]], [2], 3], + True, + [1, 2, 3] + ), + ( + [[1]], + False, + [1] + ), + ( + [[1]], + True, + 1 + ), + ( + 1, + False, + [1] + ), + ( + 1, + True, + 1 + ), +] + +RENDER_VALID = [ + ( + { + "NIC": {"NAME": "NIC0", "NETWORK_ID": 0}, + "CPU": 1, + "MEMORY": 1024, + }, + textwrap.dedent(''' + CPU="1" + MEMORY="1024" + NIC=[NAME="NIC0",NETWORK_ID="0"] + ''').strip() + ), + ( + { + "NIC": [ + {"NAME": "NIC0", "NETWORK_ID": 0}, + {"NAME": "NIC1", "NETWORK_ID": 1}, + ], + "CPU": 1, + "MEMORY": 1024, + }, + textwrap.dedent(''' + CPU="1" + MEMORY="1024" + NIC=[NAME="NIC0",NETWORK_ID="0"] + NIC=[NAME="NIC1",NETWORK_ID="1"] + ''').strip() + ), + ( + { + 'EMPTY_VALUE': None, + 'SCHED_REQUIREMENTS': 'CLUSTER_ID="100"', + 'BACKSLASH_ESCAPED': "this is escaped: \\n; this isn't: \"\nend", + }, + textwrap.dedent(''' + BACKSLASH_ESCAPED="this is escaped: \\\\n; this isn't: \\" + end" + SCHED_REQUIREMENTS="CLUSTER_ID=\\"100\\"" + ''').strip() + ), +] + + +@pytest.mark.parametrize('to_flatten,extract,expected_result', FLATTEN_VALID) +def test_flatten(to_flatten, extract, expected_result): + result = flatten(to_flatten, extract) + assert result == expected_result, repr(result) + + +@pytest.mark.parametrize('to_render,expected_result', RENDER_VALID) +def test_render(to_render, expected_result): + result = render(to_render) + assert result == expected_result, repr(result) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_saslprep.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_saslprep.py new file mode 100644 index 000000000..d7a302248 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_saslprep.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Andrey Tuzhilin +# Copyright (c) 2020, Andrew Klychkov (@Andersson007) +# 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 + +from ansible_collections.community.general.plugins.module_utils.saslprep import saslprep + + +VALID = [ + (u'', u''), + (u'\u00A0', u' '), + (u'a', u'a'), + (u'й', u'й'), + (u'\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9', u'\u30DE\u30C8\u30EA\u30C3\u30AF\u30B9'), + (u'The\u00ADM\u00AAtr\u2168', u'TheMatrIX'), + (u'I\u00ADX', u'IX'), + (u'user', u'user'), + (u'USER', u'USER'), + (u'\u00AA', u'a'), + (u'\u2168', u'IX'), + (u'\u05BE\u00A0\u05BE', u'\u05BE\u0020\u05BE'), +] + +INVALID = [ + (None, TypeError), + (b'', TypeError), + (u'\u0221', ValueError), + (u'\u0007', ValueError), + (u'\u0627\u0031', ValueError), + (u'\uE0001', ValueError), + (u'\uE0020', ValueError), + (u'\uFFF9', ValueError), + (u'\uFDD0', ValueError), + (u'\u0000', ValueError), + (u'\u06DD', ValueError), + (u'\uFFFFD', ValueError), + (u'\uD800', ValueError), + (u'\u200E', ValueError), + (u'\u05BE\u00AA\u05BE', ValueError), +] + + +@pytest.mark.parametrize('source,target', VALID) +def test_saslprep_conversions(source, target): + assert saslprep(source) == target + + +@pytest.mark.parametrize('source,exception', INVALID) +def test_saslprep_exceptions(source, exception): + with pytest.raises(exception) as ex: + saslprep(source) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_utm_utils.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_utm_utils.py new file mode 100644 index 000000000..1cab58d63 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_utm_utils.py @@ -0,0 +1,48 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c) 2018, Johannes Brunswicker +# +# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause) +# SPDX-License-Identifier: BSD-2-Clause + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.community.general.plugins.module_utils.utm_utils import UTM + + +class FakeModule: + def __init__(self, params): + self.params = params + + +def test_combine_headers_returns_only_default(): + expected = {"Accept": "application/json", "Content-type": "application/json"} + module = FakeModule( + params={'utm_protocol': 'utm_protocol', 'utm_host': 'utm_host', 'utm_port': 1234, 'utm_token': 'utm_token', + 'name': 'FakeName', 'headers': {}}) + result = UTM(module, "endpoint", [])._combine_headers() + assert result == expected + + +def test_combine_headers_returns_only_default2(): + expected = {"Accept": "application/json", "Content-type": "application/json"} + module = FakeModule( + params={'utm_protocol': 'utm_protocol', 'utm_host': 'utm_host', 'utm_port': 1234, 'utm_token': 'utm_token', + 'name': 'FakeName'}) + result = UTM(module, "endpoint", [])._combine_headers() + assert result == expected + + +def test_combine_headers_returns_combined(): + expected = {"Accept": "application/json", "Content-type": "application/json", + "extraHeader": "extraHeaderValue"} + module = FakeModule(params={'utm_protocol': 'utm_protocol', 'utm_host': 'utm_host', 'utm_port': 1234, + 'utm_token': 'utm_token', 'name': 'FakeName', + "headers": {"extraHeader": "extraHeaderValue"}}) + result = UTM(module, "endpoint", [])._combine_headers() + assert result == expected diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeAnsibleModule.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeAnsibleModule.py new file mode 100644 index 000000000..bdcc21793 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeAnsibleModule.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + + +class AnsibleModuleException(Exception): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + +class ExitJsonException(AnsibleModuleException): + pass + + +class FailJsonException(AnsibleModuleException): + pass + + +class FakeAnsibleModule: + def __init__(self, params=None, check_mode=False): + self.params = params + self.check_mode = check_mode + + def exit_json(self, *args, **kwargs): + raise ExitJsonException(*args, **kwargs) + + def fail_json(self, *args, **kwargs): + raise FailJsonException(*args, **kwargs) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeXenAPI.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeXenAPI.py new file mode 100644 index 000000000..bc9d69c77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/FakeXenAPI.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +FAKE_API_VERSION = "1.1" + + +class Failure(Exception): + def __init__(self, details): + self.details = details + + def __str__(self): + return str(self.details) + + +class Session(object): + def __init__(self, uri, transport=None, encoding=None, verbose=0, + allow_none=1, ignore_ssl=False): + + self.transport = transport + self._session = None + self.last_login_method = None + self.last_login_params = None + self.API_version = FAKE_API_VERSION + + def _get_api_version(self): + return FAKE_API_VERSION + + def _login(self, method, params): + self._session = "OpaqueRef:fake-xenapi-session-ref" + self.last_login_method = method + self.last_login_params = params + self.API_version = self._get_api_version() + + def _logout(self): + self._session = None + self.last_login_method = None + self.last_login_params = None + self.API_version = FAKE_API_VERSION + + def xenapi_request(self, methodname, params): + if methodname.startswith('login'): + self._login(methodname, params) + return None + elif methodname == 'logout' or methodname == 'session.logout': + self._logout() + return None + else: + # Should be patched with mocker.patch(). + return None + + def __getattr__(self, name): + if name == 'handle': + return self._session + elif name == 'xenapi': + # Should be patched with mocker.patch(). + return None + elif name.startswith('login') or name.startswith('slave_local'): + return lambda *params: self._login(name, params) + elif name == 'logout': + return self._logout + + +def xapi_local(): + return Session("http://_var_lib_xcp_xapi/") diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/common.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/common.py new file mode 100644 index 000000000..0aee3197e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/common.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + + +def fake_xenapi_ref(xenapi_class): + return "OpaqueRef:fake-xenapi-%s-ref" % xenapi_class + + +testcase_bad_xenapi_refs = { + "params": [ + None, + '', + 'OpaqueRef:NULL', + ], + "ids": [ + 'none', + 'empty', + 'ref-null', + ], +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/conftest.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/conftest.py new file mode 100644 index 000000000..3fcea5561 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/conftest.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 importlib +import os +import json +import pytest + +from .FakeAnsibleModule import FakeAnsibleModule +from ansible.module_utils import six +from mock import MagicMock + + +@pytest.fixture +def fake_ansible_module(request): + """Returns fake AnsibleModule with fake module params.""" + if hasattr(request, 'param'): + return FakeAnsibleModule(request.param) + else: + params = { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "validate_certs": True, + } + + return FakeAnsibleModule(params) + + +@pytest.fixture(autouse=True) +def XenAPI(): + """Imports and returns fake XenAPI module.""" + + # Import of fake XenAPI module is wrapped by fixture so that it does not + # affect other unit tests which could potentially also use XenAPI module. + + # First we use importlib.import_module() to import the module and assign + # it to a local symbol. + fake_xenapi = importlib.import_module('ansible_collections.community.general.tests.unit.plugins.module_utils.xenserver.FakeXenAPI') + + # Now we populate Python module cache with imported fake module using the + # original module name (XenAPI). That way, any 'import XenAPI' statement + # will just load already imported fake module from the cache. + sys.modules['XenAPI'] = fake_xenapi + + return fake_xenapi + + +@pytest.fixture(autouse=True) +def xenserver(XenAPI): + """Imports and returns xenserver module util.""" + + # Since we are wrapping fake XenAPI module inside a fixture, all modules + # that depend on it have to be imported inside a test function. To make + # this easier to handle and remove some code repetition, we wrap the import + # of xenserver module util with a fixture. + from ansible_collections.community.general.plugins.module_utils import xenserver + + return xenserver + + +@pytest.fixture +def mock_xenapi_failure(XenAPI, mocker): + """ + Returns mock object that raises XenAPI.Failure on any XenAPI + method call. + """ + fake_error_msg = "Fake XAPI method call error!" + + # We need to use our MagicMock based class that passes side_effect to its + # children because calls to xenapi methods can generate an arbitrary + # hierarchy of mock objects. Any such object when called should use the + # same side_effect as its parent mock object. + class MagicMockSideEffect(MagicMock): + def _get_child_mock(self, **kw): + child_mock = super(MagicMockSideEffect, self)._get_child_mock(**kw) + child_mock.side_effect = self.side_effect + return child_mock + + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', new=MagicMockSideEffect(), create=True) + mocked_xenapi.side_effect = XenAPI.Failure(fake_error_msg) + + return mocked_xenapi, fake_error_msg + + +@pytest.fixture +def fixture_data_from_file(request): + """Loads fixture data from files.""" + if not hasattr(request, 'param'): + return {} + + fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') + fixture_data = {} + + if isinstance(request.param, six.string_types): + request.param = [request.param] + + for fixture_name in request.param: + path = os.path.join(fixture_path, fixture_name) + + with open(path) as f: + data = f.read() + + try: + data = json.loads(data) + except Exception: + pass + + fixture_data[fixture_name] = data + + return fixture_data diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json new file mode 100644 index 000000000..add2dcf4b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json @@ -0,0 +1,73 @@ +{ + "cdrom": { + "type": "none" + }, + "customization_agent": "native", + "disks": [ + { + "name": "ansible-test-vm-1-C", + "name_desc": "C:\\", + "os_device": "xvda", + "size": 42949672960, + "sr": "Ansible Test Storage 1", + "sr_uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "vbd_userdevice": "0" + } + ], + "domid": "143", + "folder": "/Ansible/Test", + "hardware": { + "memory_mb": 2048, + "num_cpu_cores_per_socket": 2, + "num_cpus": 2 + }, + "home_server": "", + "is_template": false, + "name": "ansible-test-vm-1", + "name_desc": "Created by Ansible", + "networks": [ + { + "gateway": "10.0.0.1", + "gateway6": "", + "ip": "10.0.0.2", + "ip6": [ + "fe80:0000:0000:0000:11e1:12c9:ef3b:75a0" + ], + "mac": "7a:a6:48:1e:31:46", + "mtu": "1500", + "name": "Host internal management network", + "netmask": "255.255.255.0", + "prefix": "24", + "prefix6": "", + "vif_device": "0" + } + ], + "other_config": { + "base_template_name": "Windows Server 2016 (64-bit)", + "folder": "/Ansible/Test", + "import_task": "OpaqueRef:e43eb71c-45d6-5351-09ff-96e4fb7d0fa5", + "install-methods": "cdrom", + "instant": "true", + "mac_seed": "366fe8e0-878b-4320-8731-90d1ed3c0b93" + }, + "platform": { + "acpi": "1", + "apic": "true", + "cores-per-socket": "2", + "device_id": "0002", + "hpet": "true", + "nx": "true", + "pae": "true", + "timeoffset": "-28800", + "vga": "std", + "videoram": "8", + "viridian": "true", + "viridian_reference_tsc": "true", + "viridian_time_ref_count": "true" + }, + "state": "poweredon", + "uuid": "81c373d7-a407-322f-911b-31386eb5215d", + "xenstore_data": { + "vm-data": "" + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json.license b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-facts.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json new file mode 100644 index 000000000..709769668 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json @@ -0,0 +1,707 @@ +{ + "SR": { + "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f": { + "PBDs": [], + "VDIs": [], + "allowed_operations": [ + "unplug", + "plug", + "pbd_create", + "update", + "pbd_destroy", + "vdi_resize", + "vdi_clone", + "scan", + "vdi_snapshot", + "vdi_mirror", + "vdi_create", + "vdi_destroy" + ], + "blobs": {}, + "clustered": false, + "content_type": "", + "current_operations": {}, + "introduced_by": "OpaqueRef:NULL", + "is_tools_sr": false, + "local_cache_enabled": false, + "name_description": "", + "name_label": "Ansible Test Storage 1", + "other_config": { + "auto-scan": "false" + }, + "physical_size": "2521133219840", + "physical_utilisation": "1551485632512", + "shared": true, + "sm_config": { + "allocation": "thick", + "devserial": "scsi-3600a098038302d353624495242443848", + "multipathable": "true", + "use_vhd": "true" + }, + "tags": [], + "type": "lvmohba", + "uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "virtual_allocation": "1556925644800" + } + }, + "VBD": { + "OpaqueRef:1c0a7c6d-09e5-9b2c-bbe3-9a73aadcff9f": { + "VDI": "OpaqueRef:NULL", + "VM": "OpaqueRef:43a1b8d4-da96-cb08-10f5-fb368abed19c", + "allowed_operations": [ + "attach", + "unpause", + "insert", + "pause" + ], + "bootable": false, + "current_operations": {}, + "currently_attached": true, + "device": "xvdd", + "empty": true, + "metrics": "OpaqueRef:1a36eae4-87c8-0945-cee9-c85a71fd843f", + "mode": "RO", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "CD", + "unpluggable": true, + "userdevice": "3", + "uuid": "e6aacd53-a2c8-649f-b405-93fcb811411a" + }, + "OpaqueRef:ea4a4088-19c3-6db6-ebdf-c3c0ee4405a3": { + "VDI": "OpaqueRef:fd20510d-e9ca-b966-3b98-4ae547dacf9a", + "VM": "OpaqueRef:43a1b8d4-da96-cb08-10f5-fb368abed19c", + "allowed_operations": [ + "attach", + "unpause", + "unplug", + "unplug_force", + "pause" + ], + "bootable": true, + "current_operations": {}, + "currently_attached": true, + "device": "xvda", + "empty": false, + "metrics": "OpaqueRef:ddbd70d4-7dde-b51e-6208-eb434b300009", + "mode": "RW", + "other_config": { + "owner": "true" + }, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "Disk", + "unpluggable": true, + "userdevice": "0", + "uuid": "ffd6de9c-c416-1d52-3e9d-3bcbf567245e" + } + }, + "VDI": { + "OpaqueRef:fd20510d-e9ca-b966-3b98-4ae547dacf9a": { + "SR": "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f", + "VBDs": [ + "OpaqueRef:ea4a4088-19c3-6db6-ebdf-c3c0ee4405a3" + ], + "allow_caching": false, + "allowed_operations": [ + "clone", + "snapshot" + ], + "crash_dumps": [], + "current_operations": {}, + "is_a_snapshot": false, + "is_tools_iso": false, + "location": "b807f67b-3f37-4a6e-ad6c-033f812ab093", + "managed": true, + "metadata_latest": false, + "metadata_of_pool": "", + "missing": false, + "name_description": "C:\\", + "name_label": "ansible-test-vm-1-C", + "on_boot": "persist", + "other_config": {}, + "parent": "OpaqueRef:NULL", + "physical_utilisation": "43041947648", + "read_only": false, + "sharable": false, + "sm_config": { + "host_OpaqueRef:07a8da76-f1cf-f3b5-a531-6b751384f770": "RW", + "read-caching-enabled-on-92ac8132-276b-4d0f-9d3a-54db51e4a438": "false", + "read-caching-reason-92ac8132-276b-4d0f-9d3a-54db51e4a438": "LICENSE_RESTRICTION", + "vdi_type": "vhd" + }, + "snapshot_of": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "storage_lock": false, + "tags": [], + "type": "system", + "uuid": "b807f67b-3f37-4a6e-ad6c-033f812ab093", + "virtual_size": "42949672960", + "xenstore_data": {} + } + }, + "VIF": { + "OpaqueRef:38da2120-6086-5043-8383-ab0a53ede42a": { + "MAC": "7a:a6:48:1e:31:46", + "MAC_autogenerated": false, + "MTU": "1500", + "VM": "OpaqueRef:43a1b8d4-da96-cb08-10f5-fb368abed19c", + "allowed_operations": [ + "attach", + "unplug" + ], + "current_operations": {}, + "currently_attached": true, + "device": "0", + "ipv4_addresses": [ + "10.0.0.2/24" + ], + "ipv4_allowed": [], + "ipv4_configuration_mode": "Static", + "ipv4_gateway": "10.0.0.1", + "ipv6_addresses": [ + "" + ], + "ipv6_allowed": [], + "ipv6_configuration_mode": "None", + "ipv6_gateway": "", + "locking_mode": "network_default", + "metrics": "OpaqueRef:15502939-df0f-0095-1ce3-e51367199d27", + "network": "OpaqueRef:8a404c5e-5673-ab69-5d6f-5a35a33b8724", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "uuid": "bd108d25-488a-f9b5-4c7b-02d40f1e38a8" + } + }, + "VM": { + "OpaqueRef:43a1b8d4-da96-cb08-10f5-fb368abed19c": { + "HVM_boot_params": { + "order": "dc" + }, + "HVM_boot_policy": "BIOS order", + "HVM_shadow_multiplier": 1.0, + "PCI_bus": "", + "PV_args": "", + "PV_bootloader": "", + "PV_bootloader_args": "", + "PV_kernel": "", + "PV_legacy_args": "", + "PV_ramdisk": "", + "VBDs": [ + "OpaqueRef:1c0a7c6d-09e5-9b2c-bbe3-9a73aadcff9f", + "OpaqueRef:ea4a4088-19c3-6db6-ebdf-c3c0ee4405a3" + ], + "VCPUs_at_startup": "2", + "VCPUs_max": "2", + "VCPUs_params": {}, + "VGPUs": [], + "VIFs": [ + "OpaqueRef:38da2120-6086-5043-8383-ab0a53ede42a" + ], + "VTPMs": [], + "actions_after_crash": "restart", + "actions_after_reboot": "restart", + "actions_after_shutdown": "destroy", + "affinity": "OpaqueRef:NULL", + "allowed_operations": [ + "changing_dynamic_range", + "migrate_send", + "pool_migrate", + "changing_VCPUs_live", + "suspend", + "hard_reboot", + "hard_shutdown", + "clean_reboot", + "clean_shutdown", + "pause", + "checkpoint", + "snapshot" + ], + "appliance": "OpaqueRef:NULL", + "attached_PCIs": [], + "bios_strings": { + "bios-vendor": "Xen", + "bios-version": "", + "hp-rombios": "", + "oem-1": "Xen", + "oem-2": "MS_VM_CERT/SHA1/bdbeb6e0a816d43fa6d3fe8aaef04c2bad9d3e3d", + "system-manufacturer": "Xen", + "system-product-name": "HVM domU", + "system-serial-number": "", + "system-version": "" + }, + "blobs": {}, + "blocked_operations": {}, + "children": [], + "consoles": [ + "OpaqueRef:4fa7d34e-1fb6-9e88-1b21-41a3c6550d8b" + ], + "crash_dumps": [], + "current_operations": {}, + "domarch": "", + "domid": "143", + "generation_id": "3274224479562869847:6952848762503845513", + "guest_metrics": "OpaqueRef:453f21be-954d-2ca8-e38e-09741e91350c", + "ha_always_run": false, + "ha_restart_priority": "", + "hardware_platform_version": "0", + "has_vendor_device": false, + "is_a_snapshot": false, + "is_a_template": false, + "is_control_domain": false, + "is_default_template": false, + "is_snapshot_from_vmpp": false, + "is_vmss_snapshot": false, + "last_boot_CPU_flags": { + "features": "17cbfbff-f7fa3223-2d93fbff-00000023-00000001-000007ab-00000000-00000000-00001000-0c000000", + "vendor": "GenuineIntel" + }, + "last_booted_record": "", + "memory_dynamic_max": "2147483648", + "memory_dynamic_min": "2147483648", + "memory_overhead": "20971520", + "memory_static_max": "2147483648", + "memory_static_min": "1073741824", + "memory_target": "2147483648", + "metrics": "OpaqueRef:6eede779-4e55-7cfb-8b8a-e4b9becf770b", + "name_description": "Created by Ansible", + "name_label": "ansible-test-vm-1", + "order": "0", + "other_config": { + "base_template_name": "Windows Server 2016 (64-bit)", + "folder": "/Ansible/Test", + "import_task": "OpaqueRef:e43eb71c-45d6-5351-09ff-96e4fb7d0fa5", + "install-methods": "cdrom", + "instant": "true", + "mac_seed": "366fe8e0-878b-4320-8731-90d1ed3c0b93" + }, + "parent": "OpaqueRef:NULL", + "platform": { + "acpi": "1", + "apic": "true", + "cores-per-socket": "2", + "device_id": "0002", + "hpet": "true", + "nx": "true", + "pae": "true", + "timeoffset": "-28800", + "vga": "std", + "videoram": "8", + "viridian": "true", + "viridian_reference_tsc": "true", + "viridian_time_ref_count": "true" + }, + "power_state": "Running", + "protection_policy": "OpaqueRef:NULL", + "recommendations": "", + "reference_label": "windows-server-2016-64bit", + "requires_reboot": false, + "resident_on": "OpaqueRef:07a8da76-f1cf-f3b5-a531-6b751384f770", + "shutdown_delay": "0", + "snapshot_info": {}, + "snapshot_metadata": "", + "snapshot_of": "OpaqueRef:NULL", + "snapshot_schedule": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "start_delay": "0", + "suspend_SR": "OpaqueRef:NULL", + "suspend_VDI": "OpaqueRef:NULL", + "tags": [], + "transportable_snapshot_id": "", + "user_version": "1", + "uuid": "81c373d7-a407-322f-911b-31386eb5215d", + "version": "0", + "xenstore_data": { + "vm-data": "" + } + } + }, + "VM_guest_metrics": { + "OpaqueRef:453f21be-954d-2ca8-e38e-09741e91350c": { + "PV_drivers_detected": true, + "PV_drivers_up_to_date": true, + "PV_drivers_version": { + "build": "1020", + "major": "7", + "micro": "0", + "minor": "1" + }, + "can_use_hotplug_vbd": "yes", + "can_use_hotplug_vif": "yes", + "disks": {}, + "last_updated": "20190113T19:40:34Z", + "live": true, + "memory": {}, + "networks": { + "0/ip": "10.0.0.2", + "0/ipv6/0": "fe80:0000:0000:0000:11e1:12c9:ef3b:75a0" + }, + "os_version": { + "distro": "windows", + "major": "6", + "minor": "2", + "name": "Microsoft Windows Server 2016 Standard|C:\\Windows|\\Device\\Harddisk0\\Partition2", + "spmajor": "0", + "spminor": "0" + }, + "other": { + "data-ts": "1", + "error": "WTSQueryUserToken : 1008 failed.", + "feature-balloon": "1", + "feature-poweroff": "1", + "feature-reboot": "1", + "feature-s3": "1", + "feature-s4": "1", + "feature-setcomputername": "1", + "feature-static-ip-setting": "1", + "feature-suspend": "1", + "feature-ts": "1", + "feature-ts2": "1", + "feature-xs-batcmd": "1", + "has-vendor-device": "0", + "platform-feature-multiprocessor-suspend": "1" + }, + "other_config": {}, + "uuid": "9ea6803f-12ca-3d6a-47b7-c90a33b67b98" + } + }, + "VM_metrics": { + "OpaqueRef:6eede779-4e55-7cfb-8b8a-e4b9becf770b": { + "VCPUs_CPU": {}, + "VCPUs_flags": {}, + "VCPUs_number": "2", + "VCPUs_params": {}, + "VCPUs_utilisation": {}, + "hvm": true, + "install_time": "20190113T19:31:47Z", + "last_updated": "19700101T00:00:00Z", + "memory_actual": "2147475456", + "nested_virt": false, + "nomigrate": false, + "other_config": {}, + "start_time": "20190113T19:38:59Z", + "state": [], + "uuid": "c67fadf7-8143-0c92-c772-cd3901c18e70" + } + }, + "host": { + "OpaqueRef:07a8da76-f1cf-f3b5-a531-6b751384f770": { + "API_version_major": "2", + "API_version_minor": "7", + "API_version_vendor": "XenSource", + "API_version_vendor_implementation": {}, + "PBDs": [], + "PCIs": [], + "PGPUs": [], + "PIFs": [], + "address": "10.0.0.1", + "allowed_operations": [ + "vm_migrate", + "provision", + "vm_resume", + "evacuate", + "vm_start" + ], + "bios_strings": {}, + "blobs": {}, + "capabilities": [ + "xen-3.0-x86_64", + "xen-3.0-x86_32p", + "hvm-3.0-x86_32", + "hvm-3.0-x86_32p", + "hvm-3.0-x86_64", + "" + ], + "chipset_info": { + "iommu": "true" + }, + "control_domain": "OpaqueRef:a2a31555-f232-822b-8f36-10d75d44b79c", + "cpu_configuration": {}, + "cpu_info": { + "cpu_count": "40", + "family": "6", + "features": "7ffefbff-bfebfbff-00000021-2c100800", + "features_hvm": "17cbfbff-f7fa3223-2d93fbff-00000023-00000001-000007ab-00000000-00000000-00001000-0c000000", + "features_pv": "17c9cbf5-f6f83203-2191cbf5-00000023-00000001-00000329-00000000-00000000-00001000-0c000000", + "flags": "fpu de tsc msr pae mce cx8 apic sep mca cmov pat clflush acpi mmx fxsr sse sse2 ht syscall nx lm constant_tsc arch_perfmon rep_good nopl nonstop_tsc eagerfpu pni pclmulqdq monitor est ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm ida arat epb pln pts dtherm fsgsbase bmi1 avx2 bmi2 erms xsaveopt cqm_llc cqm_occup_llc", + "model": "63", + "modelname": "Intel(R) Xeon(R) CPU E5-2660 v3 @ 2.60GHz", + "socket_count": "2", + "speed": "2597.064", + "stepping": "2", + "vendor": "GenuineIntel" + }, + "crash_dump_sr": "OpaqueRef:ed72d7bf-4e53-67fc-17f5-e27b203042ba", + "crashdumps": [], + "current_operations": {}, + "display": "enabled", + "edition": "free", + "enabled": true, + "external_auth_configuration": {}, + "external_auth_service_name": "", + "external_auth_type": "", + "features": [], + "guest_VCPUs_params": {}, + "ha_network_peers": [], + "ha_statefiles": [], + "host_CPUs": [ + "OpaqueRef:f7e744f6-a6f9-c460-999a-c27e1395e2e0", + "OpaqueRef:f6e5dcf0-0453-8f3f-88c1-7ad6e2ef3dd1", + "OpaqueRef:f27a52fb-5feb-173d-1a07-d1735a83c2cc", + "OpaqueRef:ed65327a-508a-ccfc-dba6-2a0175cb2432", + "OpaqueRef:e41d2f2a-fe9e-72cb-8104-b22d6d314b13", + "OpaqueRef:e1988469-b814-5d10-17a6-bfd7c62d2b5f", + "OpaqueRef:d73967dc-b8d8-b47b-39f4-d599fdcabf55", + "OpaqueRef:cba9ebd9-40dc-0611-d1bb-aa661bd0bf70", + "OpaqueRef:c53d3110-4085-60af-8300-d879818789f7", + "OpaqueRef:bee0cf87-7df6-79a6-94e8-36f98e69ad20", + "OpaqueRef:bde28e83-213f-0e65-b6ad-0ae1ecebb98d", + "OpaqueRef:bbfefe67-f65f-98cb-c3fc-cb8ea0588006", + "OpaqueRef:b38ac595-afea-0ca0-49a0-9f5ef2368e3b", + "OpaqueRef:b14ef333-78b1-193d-02da-dc9bfed36912", + "OpaqueRef:afd478bf-57b9-0c79-f257-50aeb81504f1", + "OpaqueRef:a307cd3a-2132-2e42-4ebc-cc1c7780736d", + "OpaqueRef:a1a9df7d-88ba-64fd-a55c-0f6472e1753f", + "OpaqueRef:a0e39c9c-3e0b-fa03-e5d0-93a09aa77393", + "OpaqueRef:9fd5719b-36ab-8e25-7756-20a496ccb331", + "OpaqueRef:9ac4195d-ac07-cfe2-bc19-27ee54cf91fb", + "OpaqueRef:98c5c00c-1e2d-e22b-842e-79e85ce07873", + "OpaqueRef:961129bf-e695-f206-7297-64f9007a64f3", + "OpaqueRef:64368b4c-3488-2808-f0b3-42f2a656df2b", + "OpaqueRef:620dabc0-d7c5-0dc8-52df-3be25194c2fb", + "OpaqueRef:5cee2759-dd8e-7e1a-0727-21e196584030", + "OpaqueRef:58f70163-863d-5787-ffbb-2416cb16ca1e", + "OpaqueRef:4462f848-f396-653d-67f9-2bed13be2c58", + "OpaqueRef:40e800c2-19db-7cd8-c045-5ae93f908cae", + "OpaqueRef:3f84278b-dec6-ded0-1a33-4daa0ce75a2f", + "OpaqueRef:3ef14992-62f6-e1f0-5715-0ee02a834a9c", + "OpaqueRef:3e274c24-c55b-06f5-2c8f-415421043ab2", + "OpaqueRef:35ff27da-f286-7b70-adc1-a200880bb79f", + "OpaqueRef:2511aa53-8660-e442-3cd2-305982d1f751", + "OpaqueRef:21d234e3-138c-81ca-9ed8-febc81b874e9", + "OpaqueRef:1f9b4ee3-dcc7-114e-b401-dc3e94c07efa", + "OpaqueRef:1b94a981-d340-dd07-41c2-b3ff3c545fed", + "OpaqueRef:197ad104-64a8-5af3-8c7a-95f3d301aadd", + "OpaqueRef:1672e747-dc4b-737b-ddcf-0a373f966012", + "OpaqueRef:12ced494-a225-7584-456b-739331bb5114", + "OpaqueRef:0139ff72-62ac-1a6a-8f6f-cb01d8a4ee92" + ], + "hostname": "ansible-test-host-1", + "license_params": { + "address1": "", + "address2": "", + "city": "", + "company": "", + "country": "", + "enable_xha": "true", + "expiry": "20291231T23:00:00Z", + "grace": "no", + "license_type": "", + "name": "", + "platform_filter": "false", + "postalcode": "", + "productcode": "", + "regular_nag_dialog": "false", + "restrict_ad": "false", + "restrict_batch_hotfix_apply": "true", + "restrict_checkpoint": "false", + "restrict_cifs": "true", + "restrict_connection": "false", + "restrict_cpu_masking": "false", + "restrict_dmc": "false", + "restrict_dr": "false", + "restrict_email_alerting": "false", + "restrict_equalogic": "false", + "restrict_export_resource_data": "true", + "restrict_gpu": "false", + "restrict_guest_agent_auto_update": "true", + "restrict_guest_ip_setting": "false", + "restrict_health_check": "false", + "restrict_historical_performance": "false", + "restrict_hotfix_apply": "false", + "restrict_integrated_gpu_passthrough": "false", + "restrict_intellicache": "false", + "restrict_lab": "false", + "restrict_live_patching": "true", + "restrict_marathon": "false", + "restrict_nested_virt": "true", + "restrict_netapp": "false", + "restrict_pci_device_for_auto_update": "true", + "restrict_pool_attached_storage": "false", + "restrict_pooling": "false", + "restrict_pvs_proxy": "true", + "restrict_qos": "false", + "restrict_rbac": "false", + "restrict_read_caching": "true", + "restrict_set_vcpus_number_live": "true", + "restrict_ssl_legacy_switch": "false", + "restrict_stage": "false", + "restrict_storage_xen_motion": "false", + "restrict_storagelink": "false", + "restrict_storagelink_site_recovery": "false", + "restrict_vgpu": "true", + "restrict_vif_locking": "false", + "restrict_vlan": "false", + "restrict_vm_memory_introspection": "true", + "restrict_vmpr": "false", + "restrict_vmss": "false", + "restrict_vss": "false", + "restrict_vswitch_controller": "false", + "restrict_web_selfservice": "true", + "restrict_web_selfservice_manager": "true", + "restrict_wlb": "true", + "restrict_xcm": "true", + "restrict_xen_motion": "false", + "serialnumber": "", + "sku_marketing_name": "Citrix XenServer", + "sku_type": "free", + "sockets": "2", + "state": "", + "version": "" + }, + "license_server": { + "address": "localhost", + "port": "27000" + }, + "local_cache_sr": "OpaqueRef:ed72d7bf-4e53-67fc-17f5-e27b203042ba", + "logging": {}, + "memory_overhead": "4606619648", + "metrics": "OpaqueRef:82b6937a-60c2-96d8-4e78-9f9a1143033f", + "name_description": "", + "name_label": "ansible-test-host-1", + "other_config": { + "agent_start_time": "1532019557.", + "boot_time": "1530023264.", + "iscsi_iqn": "iqn.2018-06.com.example:c8bac750", + "last_blob_sync_time": "1547394076.36", + "multipathhandle": "dmp", + "multipathing": "true" + }, + "patches": [ + "OpaqueRef:f74ca18d-cfb7-e4fe-e5c4-819843de11e2", + "OpaqueRef:f53ff05e-8dd8-3a15-d3b0-8dcf6004fbe2", + "OpaqueRef:ed7f38da-1a50-a48b-60bf-933cabe8d7bc", + "OpaqueRef:e7bb1462-51a5-1aaf-3b56-11b8ebd83a94", + "OpaqueRef:d87b343b-6ba3-db8b-b80e-e02319ba5924", + "OpaqueRef:ccb00450-ed04-4eaa-e6d7-130ef3722374", + "OpaqueRef:b79b8864-11d9-1d5f-09e5-a66d7b64b9e2", + "OpaqueRef:9bebcc7d-61ae-126b-3be0-9156026e586f", + "OpaqueRef:740a1156-b991-00b8-ef50-fdbb22a4d911", + "OpaqueRef:71def430-754b-2bfb-6c93-ec3b67b754e4", + "OpaqueRef:6c73b00d-df66-1740-9578-2b14e46297ba", + "OpaqueRef:6a53d2ae-3d6b-32ed-705f-fd53f1304470", + "OpaqueRef:35a67684-b094-1c77-beff-8237d87c7a27", + "OpaqueRef:33da42c2-c421-9859-79b7-ce9b6c394a1b", + "OpaqueRef:2baa6b4b-9bbe-c1b2-23ce-c8c831ac581d", + "OpaqueRef:2ac3beea-dee2-44e7-9f67-5fd216e593a0", + "OpaqueRef:1bd8f24b-3190-6e7a-b36e-e2998197d062", + "OpaqueRef:1694ea26-4930-6ca1-036e-273438375de9", + "OpaqueRef:09813f03-0c6f-a6af-768f-ef4cdde2c641" + ], + "power_on_config": {}, + "power_on_mode": "", + "resident_VMs": [], + "sched_policy": "credit", + "software_version": { + "build_number": "release/falcon/master/8", + "date": "2017-05-11", + "db_schema": "5.120", + "dbv": "2017.0517", + "hostname": "f7d02093adae", + "linux": "4.4.0+10", + "network_backend": "openvswitch", + "platform_name": "XCP", + "platform_version": "2.3.0", + "product_brand": "XenServer", + "product_version": "7.2.0", + "product_version_text": "7.2", + "product_version_text_short": "7.2", + "xapi": "1.9", + "xen": "4.7.5-2.12", + "xencenter_max": "2.7", + "xencenter_min": "2.7" + }, + "ssl_legacy": true, + "supported_bootloaders": [ + "pygrub", + "eliloader" + ], + "suspend_image_sr": "OpaqueRef:ed72d7bf-4e53-67fc-17f5-e27b203042ba", + "tags": [], + "updates": [ + "OpaqueRef:b71938bf-4c4f-eb17-7e78-588e71297a74", + "OpaqueRef:91cfa47b-52f9-a4e3-4e78-52e3eb3e5141", + "OpaqueRef:e2209ae9-5362-3a20-f691-9294144e49f2", + "OpaqueRef:6ac77a0f-f079-8067-85cc-c9ae2f8dcca9", + "OpaqueRef:a17e721d-faf4-6ad1-c617-dd4899279534", + "OpaqueRef:6c9b814c-e1c2-b8be-198f-de358686b10a", + "OpaqueRef:fbaabbfe-88d5-d89b-5b3f-d6374601ca71", + "OpaqueRef:9eccc765-9726-d220-96b1-2e85adf77ecc", + "OpaqueRef:204558d7-dce0-2304-bdc5-80ec5fd7e3c3", + "OpaqueRef:65b14ae7-f440-0c4d-4af9-c7946b90fd2f", + "OpaqueRef:0760c608-b02e-743a-18a1-fa8f205374d6", + "OpaqueRef:1ced32ca-fec4-8b44-0e8f-753c97f2d93f", + "OpaqueRef:3fffd7c7-f4d1-6b03-a5b8-d75211bb7b8f", + "OpaqueRef:01befb95-412e-e9dd-5b5d-edd50df61cb1", + "OpaqueRef:a3f9481e-fe3d-1f00-235f-44d404f51128", + "OpaqueRef:507ee5fc-59d3-e635-21d5-98a5cace4bf2", + "OpaqueRef:7b4b5da1-54af-d0c4-3fea-394b4257bffe", + "OpaqueRef:f61edc83-91d9-a161-113f-00c110196238", + "OpaqueRef:7efce157-9b93-d116-f3f8-7eb0c6fb1a79" + ], + "updates_requiring_reboot": [], + "uuid": "92ac8132-276b-4d0f-9d3a-54db51e4a438", + "virtual_hardware_platform_versions": [ + "0", + "1", + "2" + ] + } + }, + "network": { + "OpaqueRef:8a404c5e-5673-ab69-5d6f-5a35a33b8724": { + "MTU": "1500", + "PIFs": [], + "VIFs": [], + "allowed_operations": [], + "assigned_ips": { + "OpaqueRef:8171dad1-f902-ec00-7ba2-9f92d8aa75ab": "169.254.0.3", + "OpaqueRef:9754a0ed-e100-d224-6a70-a55a9c2cedf9": "169.254.0.2" + }, + "blobs": {}, + "bridge": "xenapi", + "current_operations": {}, + "default_locking_mode": "unlocked", + "managed": true, + "name_description": "Network on which guests will be assigned a private link-local IP address which can be used to talk XenAPI", + "name_label": "Host internal management network", + "other_config": { + "ip_begin": "169.254.0.1", + "ip_end": "169.254.255.254", + "is_guest_installer_network": "true", + "is_host_internal_management_network": "true", + "netmask": "255.255.0.0" + }, + "tags": [], + "uuid": "dbb96525-944f-0d1a-54ed-e65cb6d07450" + } + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json.license b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-1-params.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json new file mode 100644 index 000000000..607212c05 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json @@ -0,0 +1,87 @@ +{ + "cdrom": { + "type": "none" + }, + "customization_agent": "custom", + "disks": [ + { + "name": "ansible-test-vm-2-root", + "name_desc": "/", + "os_device": "xvda", + "size": 10737418240, + "sr": "Ansible Test Storage 1", + "sr_uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "vbd_userdevice": "0" + }, + { + "name": "ansible-test-vm-2-mysql", + "name_desc": "/var/lib/mysql", + "os_device": "xvdb", + "size": 1073741824, + "sr": "Ansible Test Storage 1", + "sr_uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "vbd_userdevice": "1" + } + ], + "domid": "140", + "folder": "/Ansible/Test", + "hardware": { + "memory_mb": 1024, + "num_cpu_cores_per_socket": 1, + "num_cpus": 1 + }, + "home_server": "ansible-test-host-2", + "is_template": false, + "name": "ansible-test-vm-2", + "name_desc": "Created by Ansible", + "networks": [ + { + "gateway": "10.0.0.1", + "gateway6": "", + "ip": "169.254.0.2", + "ip6": [], + "mac": "16:87:31:70:d6:31", + "mtu": "1500", + "name": "Host internal management network", + "netmask": "255.255.255.0", + "prefix": "24", + "prefix6": "", + "vif_device": "0" + } + ], + "other_config": { + "base_template_name": "CentOS 7", + "folder": "/Ansible/Test", + "import_task": "OpaqueRef:cf1402d3-b6c1-d908-fe62-06502e3b311a", + "install-methods": "cdrom,nfs,http,ftp", + "instant": "true", + "linux_template": "true", + "mac_seed": "0ab46664-f519-5383-166e-e4ea485ede7d" + }, + "platform": { + "acpi": "1", + "apic": "true", + "cores-per-socket": "1", + "device_id": "0001", + "nx": "true", + "pae": "true", + "timeoffset": "0", + "vga": "std", + "videoram": "8", + "viridian": "false" + }, + "state": "poweredon", + "uuid": "0a05d5ad-3e4b-f0dc-6101-8c56623958bc", + "xenstore_data": { + "vm-data": "", + "vm-data/networks": "", + "vm-data/networks/0": "", + "vm-data/networks/0/gateway": "10.0.0.1", + "vm-data/networks/0/ip": "10.0.0.3", + "vm-data/networks/0/mac": "16:87:31:70:d6:31", + "vm-data/networks/0/name": "Host internal management network", + "vm-data/networks/0/netmask": "255.255.255.0", + "vm-data/networks/0/prefix": "24", + "vm-data/networks/0/type": "static" + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json.license b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-facts.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json new file mode 100644 index 000000000..10615f40a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json @@ -0,0 +1,771 @@ +{ + "SR": { + "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f": { + "PBDs": [], + "VDIs": [], + "allowed_operations": [ + "unplug", + "plug", + "pbd_create", + "update", + "pbd_destroy", + "vdi_resize", + "vdi_clone", + "scan", + "vdi_snapshot", + "vdi_mirror", + "vdi_create", + "vdi_destroy" + ], + "blobs": {}, + "clustered": false, + "content_type": "", + "current_operations": {}, + "introduced_by": "OpaqueRef:NULL", + "is_tools_sr": false, + "local_cache_enabled": false, + "name_description": "", + "name_label": "Ansible Test Storage 1", + "other_config": { + "auto-scan": "false" + }, + "physical_size": "2521133219840", + "physical_utilisation": "1551485632512", + "shared": true, + "sm_config": { + "allocation": "thick", + "devserial": "scsi-3600a098038302d353624495242443848", + "multipathable": "true", + "use_vhd": "true" + }, + "tags": [], + "type": "lvmohba", + "uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "virtual_allocation": "1556925644800" + } + }, + "VBD": { + "OpaqueRef:510e214e-f0ba-3bc9-7834-a4f4d3fa33ef": { + "VDI": "OpaqueRef:NULL", + "VM": "OpaqueRef:08632af0-473e-5106-f400-7910229e49be", + "allowed_operations": [ + "attach", + "unpause", + "insert", + "pause" + ], + "bootable": false, + "current_operations": {}, + "currently_attached": true, + "device": "xvdd", + "empty": true, + "metrics": "OpaqueRef:1075bebe-ba71-66ef-ba30-8afbc83bc6b5", + "mode": "RO", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "CD", + "unpluggable": true, + "userdevice": "3", + "uuid": "79ee1d8e-944b-3bfd-ba4c-a0c165d84f3d" + }, + "OpaqueRef:6bc2c353-f132-926d-6e9b-e4d1d55a3760": { + "VDI": "OpaqueRef:102bef39-b134-d23a-9a50-490e1dbca8f7", + "VM": "OpaqueRef:08632af0-473e-5106-f400-7910229e49be", + "allowed_operations": [ + "attach", + "unpause", + "pause" + ], + "bootable": true, + "current_operations": {}, + "currently_attached": true, + "device": "xvda", + "empty": false, + "metrics": "OpaqueRef:1c71ccde-d7e9-10fb-569c-993b880fa790", + "mode": "RW", + "other_config": { + "owner": "" + }, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "Disk", + "unpluggable": false, + "userdevice": "0", + "uuid": "932fdf6d-7ac5-45e8-a48e-694af75726f1" + }, + "OpaqueRef:9bd6decd-2e55-b55e-387d-c40aa67ff151": { + "VDI": "OpaqueRef:87b45ac6-af36-f4fd-6ebd-a08bed9001e4", + "VM": "OpaqueRef:08632af0-473e-5106-f400-7910229e49be", + "allowed_operations": [ + "attach", + "unpause", + "unplug", + "unplug_force", + "pause" + ], + "bootable": false, + "current_operations": {}, + "currently_attached": true, + "device": "xvdb", + "empty": false, + "metrics": "OpaqueRef:b8424146-d3ea-4850-db9a-47f0059c10ac", + "mode": "RW", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "Disk", + "unpluggable": true, + "userdevice": "1", + "uuid": "c0c1e648-3690-e1fb-9f47-24b4df0cb458" + } + }, + "VDI": { + "OpaqueRef:102bef39-b134-d23a-9a50-490e1dbca8f7": { + "SR": "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f", + "VBDs": [ + "OpaqueRef:6bc2c353-f132-926d-6e9b-e4d1d55a3760" + ], + "allow_caching": false, + "allowed_operations": [ + "clone", + "snapshot" + ], + "crash_dumps": [], + "current_operations": {}, + "is_a_snapshot": false, + "is_tools_iso": false, + "location": "fa1202b8-326f-4235-802e-fafbed66b26b", + "managed": true, + "metadata_latest": false, + "metadata_of_pool": "", + "missing": false, + "name_description": "/", + "name_label": "ansible-test-vm-2-root", + "on_boot": "persist", + "other_config": {}, + "parent": "OpaqueRef:NULL", + "physical_utilisation": "10766778368", + "read_only": false, + "sharable": false, + "sm_config": { + "host_OpaqueRef:e87be804-57a1-532e-56ac-6c4910957be0": "RW", + "read-caching-enabled-on-dff6702e-bcb6-4704-8dd4-952e8c883365": "false", + "read-caching-reason-dff6702e-bcb6-4704-8dd4-952e8c883365": "LICENSE_RESTRICTION", + "vdi_type": "vhd" + }, + "snapshot_of": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "storage_lock": false, + "tags": [], + "type": "system", + "uuid": "fa1202b8-326f-4235-802e-fafbed66b26b", + "virtual_size": "10737418240", + "xenstore_data": {} + }, + "OpaqueRef:87b45ac6-af36-f4fd-6ebd-a08bed9001e4": { + "SR": "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f", + "VBDs": [ + "OpaqueRef:9bd6decd-2e55-b55e-387d-c40aa67ff151" + ], + "allow_caching": false, + "allowed_operations": [ + "clone", + "snapshot" + ], + "crash_dumps": [], + "current_operations": {}, + "is_a_snapshot": false, + "is_tools_iso": false, + "location": "ab3a4d72-f498-4687-86ce-ca937046db76", + "managed": true, + "metadata_latest": false, + "metadata_of_pool": "", + "missing": false, + "name_description": "/var/lib/mysql", + "name_label": "ansible-test-vm-2-mysql", + "on_boot": "persist", + "other_config": {}, + "parent": "OpaqueRef:NULL", + "physical_utilisation": "1082130432", + "read_only": false, + "sharable": false, + "sm_config": { + "host_OpaqueRef:e87be804-57a1-532e-56ac-6c4910957be0": "RW", + "read-caching-enabled-on-dff6702e-bcb6-4704-8dd4-952e8c883365": "false", + "read-caching-reason-dff6702e-bcb6-4704-8dd4-952e8c883365": "LICENSE_RESTRICTION", + "vdi_type": "vhd" + }, + "snapshot_of": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "storage_lock": false, + "tags": [], + "type": "user", + "uuid": "ab3a4d72-f498-4687-86ce-ca937046db76", + "virtual_size": "1073741824", + "xenstore_data": {} + } + }, + "VIF": { + "OpaqueRef:9754a0ed-e100-d224-6a70-a55a9c2cedf9": { + "MAC": "16:87:31:70:d6:31", + "MAC_autogenerated": false, + "MTU": "1500", + "VM": "OpaqueRef:08632af0-473e-5106-f400-7910229e49be", + "allowed_operations": [ + "attach", + "unplug" + ], + "current_operations": {}, + "currently_attached": true, + "device": "0", + "ipv4_addresses": [], + "ipv4_allowed": [], + "ipv4_configuration_mode": "None", + "ipv4_gateway": "", + "ipv6_addresses": [], + "ipv6_allowed": [], + "ipv6_configuration_mode": "None", + "ipv6_gateway": "", + "locking_mode": "network_default", + "metrics": "OpaqueRef:d74d5f20-f0ab-ee36-9a74-496ffb994232", + "network": "OpaqueRef:8a404c5e-5673-ab69-5d6f-5a35a33b8724", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "uuid": "07b70134-9396-94fc-5105-179b430ce4f8" + } + }, + "VM": { + "OpaqueRef:08632af0-473e-5106-f400-7910229e49be": { + "HVM_boot_params": { + "order": "cdn" + }, + "HVM_boot_policy": "BIOS order", + "HVM_shadow_multiplier": 1.0, + "PCI_bus": "", + "PV_args": "", + "PV_bootloader": "", + "PV_bootloader_args": "", + "PV_kernel": "", + "PV_legacy_args": "", + "PV_ramdisk": "", + "VBDs": [ + "OpaqueRef:510e214e-f0ba-3bc9-7834-a4f4d3fa33ef", + "OpaqueRef:9bd6decd-2e55-b55e-387d-c40aa67ff151", + "OpaqueRef:6bc2c353-f132-926d-6e9b-e4d1d55a3760" + ], + "VCPUs_at_startup": "1", + "VCPUs_max": "1", + "VCPUs_params": {}, + "VGPUs": [], + "VIFs": [ + "OpaqueRef:9754a0ed-e100-d224-6a70-a55a9c2cedf9" + ], + "VTPMs": [], + "actions_after_crash": "restart", + "actions_after_reboot": "restart", + "actions_after_shutdown": "destroy", + "affinity": "OpaqueRef:e87be804-57a1-532e-56ac-6c4910957be0", + "allowed_operations": [ + "changing_dynamic_range", + "migrate_send", + "pool_migrate", + "changing_VCPUs_live", + "suspend", + "hard_reboot", + "hard_shutdown", + "clean_reboot", + "clean_shutdown", + "pause", + "checkpoint", + "snapshot" + ], + "appliance": "OpaqueRef:NULL", + "attached_PCIs": [], + "bios_strings": { + "bios-vendor": "Xen", + "bios-version": "", + "hp-rombios": "", + "oem-1": "Xen", + "oem-2": "MS_VM_CERT/SHA1/bdbeb6e0a816d43fa6d3fe8aaef04c2bad9d3e3d", + "system-manufacturer": "Xen", + "system-product-name": "HVM domU", + "system-serial-number": "", + "system-version": "" + }, + "blobs": {}, + "blocked_operations": {}, + "children": [], + "consoles": [ + "OpaqueRef:2a24e023-a856-de30-aea3-2024bacdc71f" + ], + "crash_dumps": [], + "current_operations": {}, + "domarch": "", + "domid": "140", + "generation_id": "", + "guest_metrics": "OpaqueRef:150d2dfa-b634-7965-92ab-31fc26382683", + "ha_always_run": false, + "ha_restart_priority": "", + "hardware_platform_version": "0", + "has_vendor_device": false, + "is_a_snapshot": false, + "is_a_template": false, + "is_control_domain": false, + "is_default_template": false, + "is_snapshot_from_vmpp": false, + "is_vmss_snapshot": false, + "last_boot_CPU_flags": { + "features": "17cbfbff-f7fa3223-2d93fbff-00000023-00000001-000007ab-00000000-00000000-00001000-0c000000", + "vendor": "GenuineIntel" + }, + "last_booted_record": "", + "memory_dynamic_max": "1073741824", + "memory_dynamic_min": "1073741824", + "memory_overhead": "11534336", + "memory_static_max": "1073741824", + "memory_static_min": "1073741824", + "memory_target": "1073741824", + "metrics": "OpaqueRef:b56b460b-6476-304d-b143-ce543ffab828", + "name_description": "Created by Ansible", + "name_label": "ansible-test-vm-2", + "order": "0", + "other_config": { + "base_template_name": "CentOS 7", + "folder": "/Ansible/Test", + "import_task": "OpaqueRef:cf1402d3-b6c1-d908-fe62-06502e3b311a", + "install-methods": "cdrom,nfs,http,ftp", + "instant": "true", + "linux_template": "true", + "mac_seed": "0ab46664-f519-5383-166e-e4ea485ede7d" + }, + "parent": "OpaqueRef:NULL", + "platform": { + "acpi": "1", + "apic": "true", + "cores-per-socket": "1", + "device_id": "0001", + "nx": "true", + "pae": "true", + "timeoffset": "0", + "vga": "std", + "videoram": "8", + "viridian": "false" + }, + "power_state": "Running", + "protection_policy": "OpaqueRef:NULL", + "recommendations": "", + "reference_label": "", + "requires_reboot": false, + "resident_on": "OpaqueRef:e87be804-57a1-532e-56ac-6c4910957be0", + "shutdown_delay": "0", + "snapshot_info": {}, + "snapshot_metadata": "", + "snapshot_of": "OpaqueRef:NULL", + "snapshot_schedule": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "start_delay": "0", + "suspend_SR": "OpaqueRef:NULL", + "suspend_VDI": "OpaqueRef:NULL", + "tags": [], + "transportable_snapshot_id": "", + "user_version": "1", + "uuid": "0a05d5ad-3e4b-f0dc-6101-8c56623958bc", + "version": "0", + "xenstore_data": { + "vm-data": "", + "vm-data/networks": "", + "vm-data/networks/0": "", + "vm-data/networks/0/gateway": "10.0.0.1", + "vm-data/networks/0/ip": "10.0.0.3", + "vm-data/networks/0/mac": "16:87:31:70:d6:31", + "vm-data/networks/0/name": "Host internal management network", + "vm-data/networks/0/netmask": "255.255.255.0", + "vm-data/networks/0/prefix": "24", + "vm-data/networks/0/type": "static" + } + } + }, + "VM_guest_metrics": { + "OpaqueRef:150d2dfa-b634-7965-92ab-31fc26382683": { + "PV_drivers_detected": true, + "PV_drivers_up_to_date": true, + "PV_drivers_version": { + "build": "90977", + "major": "6", + "micro": "0", + "minor": "5" + }, + "can_use_hotplug_vbd": "unspecified", + "can_use_hotplug_vif": "unspecified", + "disks": {}, + "last_updated": "20190113T19:36:26Z", + "live": true, + "memory": {}, + "networks": { + "0/ip": "169.254.0.2" + }, + "os_version": { + "distro": "centos", + "major": "7", + "minor": "2", + "name": "CentOS Linux release 7.2.1511 (Core)", + "uname": "3.10.0-327.22.2.el7.x86_64" + }, + "other": { + "feature-balloon": "1", + "feature-shutdown": "1", + "feature-suspend": "1", + "feature-vcpu-hotplug": "1", + "has-vendor-device": "0", + "platform-feature-multiprocessor-suspend": "1" + }, + "other_config": {}, + "uuid": "5c9d1be5-7eee-88f2-46c3-df1d44f9cdb5" + } + }, + "VM_metrics": { + "OpaqueRef:b56b460b-6476-304d-b143-ce543ffab828": { + "VCPUs_CPU": {}, + "VCPUs_flags": {}, + "VCPUs_number": "1", + "VCPUs_params": {}, + "VCPUs_utilisation": {}, + "hvm": true, + "install_time": "20190113T19:32:46Z", + "last_updated": "19700101T00:00:00Z", + "memory_actual": "1073729536", + "nested_virt": false, + "nomigrate": false, + "other_config": {}, + "start_time": "20190113T19:35:15Z", + "state": [], + "uuid": "876dd44c-aad1-97bf-9ee5-4cd58eac7163" + } + }, + "host": { + "OpaqueRef:e87be804-57a1-532e-56ac-6c4910957be0": { + "API_version_major": "2", + "API_version_minor": "7", + "API_version_vendor": "XenSource", + "API_version_vendor_implementation": {}, + "PBDs": [], + "PCIs": [], + "PGPUs": [], + "PIFs": [], + "address": "10.0.0.1", + "allowed_operations": [ + "vm_migrate", + "provision", + "vm_resume", + "evacuate", + "vm_start" + ], + "bios_strings": {}, + "blobs": {}, + "capabilities": [ + "xen-3.0-x86_64", + "xen-3.0-x86_32p", + "hvm-3.0-x86_32", + "hvm-3.0-x86_32p", + "hvm-3.0-x86_64", + "" + ], + "chipset_info": { + "iommu": "true" + }, + "control_domain": "OpaqueRef:ffcc92a1-8fde-df6f-a501-44b37811286b", + "cpu_configuration": {}, + "cpu_info": { + "cpu_count": "40", + "family": "6", + "features": "7ffefbff-bfebfbff-00000021-2c100800", + "features_hvm": "17cbfbff-f7fa3223-2d93fbff-00000023-00000001-000007ab-00000000-00000000-00001000-0c000000", + "features_pv": "17c9cbf5-f6f83203-2191cbf5-00000023-00000001-00000329-00000000-00000000-00001000-0c000000", + "flags": "fpu de tsc msr pae mce cx8 apic sep mca cmov pat clflush acpi mmx fxsr sse sse2 ht syscall nx lm constant_tsc arch_perfmon rep_good nopl nonstop_tsc eagerfpu pni pclmulqdq monitor est ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm ida arat epb pln pts dtherm fsgsbase bmi1 avx2 bmi2 erms xsaveopt cqm_llc cqm_occup_llc", + "model": "63", + "modelname": "Intel(R) Xeon(R) CPU E5-2660 v3 @ 2.60GHz", + "socket_count": "2", + "speed": "2597.070", + "stepping": "2", + "vendor": "GenuineIntel" + }, + "crash_dump_sr": "OpaqueRef:0b984cec-a36c-ce84-7b34-9f0088352d55", + "crashdumps": [], + "current_operations": {}, + "display": "enabled", + "edition": "free", + "enabled": true, + "external_auth_configuration": {}, + "external_auth_service_name": "", + "external_auth_type": "", + "features": [], + "guest_VCPUs_params": {}, + "ha_network_peers": [], + "ha_statefiles": [], + "host_CPUs": [ + "OpaqueRef:ec3ba9c4-9b57-236b-3eaa-b157affc1621", + "OpaqueRef:e6de7ab3-f4ad-f271-e51b-e3d8c041d3fb", + "OpaqueRef:e519ef88-bf41-86ac-16b3-c178cb4b78b1", + "OpaqueRef:e48f1bc1-98ba-89e5-ab69-821c625f7f82", + "OpaqueRef:e2659936-3de6-dbca-cc44-4af50960b2b7", + "OpaqueRef:d0da1e31-20ac-4aff-8897-e80df8200648", + "OpaqueRef:cec473ba-41a8-439d-b397-be0c60467b5d", + "OpaqueRef:ce88014d-b06c-c959-0624-04d79b791885", + "OpaqueRef:c656ca58-41fe-3689-d322-174aa5798beb", + "OpaqueRef:c0a21f14-8f46-19de-1cf4-530a34c4aa17", + "OpaqueRef:bf70c061-7b45-0497-7ef6-65a236e898e8", + "OpaqueRef:b7a2ba0f-f11b-3633-ad47-4f5f76a600a8", + "OpaqueRef:b4fef1fa-3aae-9790-f47e-6a17f645339c", + "OpaqueRef:b4594721-f8f4-4475-61c5-4efeec1733f1", + "OpaqueRef:9dcba36f-c29f-478f-f578-d1ea347410a6", + "OpaqueRef:987897e8-1184-917e-6a5f-e205d0c739e5", + "OpaqueRef:90f06d64-be18-7fdf-36ba-bbd696a26cf3", + "OpaqueRef:90150bc1-e604-4cd4-35ad-9cfa8e985de3", + "OpaqueRef:838f4ad4-8ad2-0d6c-a74e-26baa461de3d", + "OpaqueRef:736fb523-d347-e8c0-089b-c9811d3c1195", + "OpaqueRef:7137b479-87d4-9097-a684-e54cc4de5d09", + "OpaqueRef:6e08fa1d-7d7b-d9be-1574-ffe95bd515fd", + "OpaqueRef:6b9e6ecd-54e5-4248-5aea-ee5b99248818", + "OpaqueRef:65d56b24-3445-b444-5125-c91e6966fd29", + "OpaqueRef:60908eca-1e5c-c938-5b76-e8ff9d8899ab", + "OpaqueRef:46e96878-c076-2164-2373-6cdd108c2436", + "OpaqueRef:40ccdaf4-6008-2b83-92cb-ca197f73433f", + "OpaqueRef:3bc8133a-ccb2-6790-152f-b3f577517751", + "OpaqueRef:38c8edd8-0621-76de-53f6-86bef2a9e05c", + "OpaqueRef:342c1bab-a211-a0eb-79a5-780bd5ad1f23", + "OpaqueRef:1e20e6d0-5502-0dff-4f17-5d35eb833af1", + "OpaqueRef:176baafa-0e63-7000-f754-25e2a6b74959", + "OpaqueRef:16cab1a2-0111-b2af-6dfe-3724b79e6b6b", + "OpaqueRef:0f213647-8362-9c5e-e99b-0ebaefc609ce", + "OpaqueRef:0e019819-b41f-0bfb-d4ee-dd5484fea9b6", + "OpaqueRef:0d39212f-82ba-190c-b304-19b3fa491fff", + "OpaqueRef:087ce3ad-3b66-ae1e-3130-3ae640dcc638", + "OpaqueRef:0730f24c-87ed-8296-8f14-3036e5ad2357", + "OpaqueRef:04c27426-4895-39a7-9ade-ef33d3721c26", + "OpaqueRef:017b27bf-0270-19e7-049a-5a9b3bb54898" + ], + "hostname": "ansible-test-host-2", + "license_params": { + "address1": "", + "address2": "", + "city": "", + "company": "", + "country": "", + "enable_xha": "true", + "expiry": "20291231T23:00:00Z", + "grace": "no", + "license_type": "", + "name": "", + "platform_filter": "false", + "postalcode": "", + "productcode": "", + "regular_nag_dialog": "false", + "restrict_ad": "false", + "restrict_batch_hotfix_apply": "true", + "restrict_checkpoint": "false", + "restrict_cifs": "true", + "restrict_connection": "false", + "restrict_cpu_masking": "false", + "restrict_dmc": "false", + "restrict_dr": "false", + "restrict_email_alerting": "false", + "restrict_equalogic": "false", + "restrict_export_resource_data": "true", + "restrict_gpu": "false", + "restrict_guest_agent_auto_update": "true", + "restrict_guest_ip_setting": "false", + "restrict_health_check": "false", + "restrict_historical_performance": "false", + "restrict_hotfix_apply": "false", + "restrict_integrated_gpu_passthrough": "false", + "restrict_intellicache": "false", + "restrict_lab": "false", + "restrict_live_patching": "true", + "restrict_marathon": "false", + "restrict_nested_virt": "true", + "restrict_netapp": "false", + "restrict_pci_device_for_auto_update": "true", + "restrict_pool_attached_storage": "false", + "restrict_pooling": "false", + "restrict_pvs_proxy": "true", + "restrict_qos": "false", + "restrict_rbac": "false", + "restrict_read_caching": "true", + "restrict_set_vcpus_number_live": "true", + "restrict_ssl_legacy_switch": "false", + "restrict_stage": "false", + "restrict_storage_xen_motion": "false", + "restrict_storagelink": "false", + "restrict_storagelink_site_recovery": "false", + "restrict_vgpu": "true", + "restrict_vif_locking": "false", + "restrict_vlan": "false", + "restrict_vm_memory_introspection": "true", + "restrict_vmpr": "false", + "restrict_vmss": "false", + "restrict_vss": "false", + "restrict_vswitch_controller": "false", + "restrict_web_selfservice": "true", + "restrict_web_selfservice_manager": "true", + "restrict_wlb": "true", + "restrict_xcm": "true", + "restrict_xen_motion": "false", + "serialnumber": "", + "sku_marketing_name": "Citrix XenServer", + "sku_type": "free", + "sockets": "2", + "state": "", + "version": "" + }, + "license_server": { + "address": "localhost", + "port": "27000" + }, + "local_cache_sr": "OpaqueRef:0b984cec-a36c-ce84-7b34-9f0088352d55", + "logging": {}, + "memory_overhead": "4865126400", + "metrics": "OpaqueRef:f55653cb-92eb-8257-f2ee-7a2d1c2d6aef", + "name_description": "", + "name_label": "ansible-test-host-2", + "other_config": { + "agent_start_time": "1532019582.", + "boot_time": "1528986759.", + "iscsi_iqn": "iqn.2018-06.com.example:87b7637d", + "last_blob_sync_time": "1547394065.41", + "multipathhandle": "dmp", + "multipathing": "true" + }, + "patches": [ + "OpaqueRef:f5bd18b6-1423-893a-5d7f-7095338e6a2d", + "OpaqueRef:eecb0b95-87fb-a53e-651c-9741efd18bb6", + "OpaqueRef:e92c9ef3-2e51-1a36-d400-9e237982b782", + "OpaqueRef:cc98226c-2c08-799e-5f15-7761a398e4a0", + "OpaqueRef:c4f35e66-d064-55a7-6946-7f4b145275a6", + "OpaqueRef:c3794494-f894-6141-b811-f37a8fe60094", + "OpaqueRef:bcf61af7-63a9-e430-5b7c-a740ba470596", + "OpaqueRef:b58ac71e-797e-6f66-71ad-fe298c94fd10", + "OpaqueRef:a2ea18fd-5343-f8db-718d-f059c2a8cce0", + "OpaqueRef:929db459-6861-c588-158f-70f763331d6d", + "OpaqueRef:92962d94-2205-f6e1-12f9-b55a99fd824d", + "OpaqueRef:65dfb07a-f90d-dad9-9ab8-1cc2b1e79afb", + "OpaqueRef:537a87c4-3bf4-969f-f06a-2dd8d3a018a2", + "OpaqueRef:32dd1de3-c9c8-bcbb-27a0-83d4a930876d", + "OpaqueRef:30a8ccc8-74a9-b31f-0403-66b117e281b6", + "OpaqueRef:24545c44-ffd1-8a28-18c6-3d008bf4d63e", + "OpaqueRef:1fcef81b-7c44-a4db-f59a-c4a147da9c49", + "OpaqueRef:1e98a240-514b-1863-5518-c771d0ebf579", + "OpaqueRef:1632cab2-b268-6ce8-4f7b-ce7fd4bfa1eb" + ], + "power_on_config": {}, + "power_on_mode": "", + "resident_VMs": [], + "sched_policy": "credit", + "software_version": { + "build_number": "release/falcon/master/8", + "date": "2017-05-11", + "db_schema": "5.120", + "dbv": "2017.0517", + "hostname": "f7d02093adae", + "linux": "4.4.0+10", + "network_backend": "openvswitch", + "platform_name": "XCP", + "platform_version": "2.3.0", + "product_brand": "XenServer", + "product_version": "7.2.0", + "product_version_text": "7.2", + "product_version_text_short": "7.2", + "xapi": "1.9", + "xen": "4.7.5-2.12", + "xencenter_max": "2.7", + "xencenter_min": "2.7" + }, + "ssl_legacy": true, + "supported_bootloaders": [ + "pygrub", + "eliloader" + ], + "suspend_image_sr": "OpaqueRef:0b984cec-a36c-ce84-7b34-9f0088352d55", + "tags": [], + "updates": [ + "OpaqueRef:7b4b5da1-54af-d0c4-3fea-394b4257bffe", + "OpaqueRef:fbaabbfe-88d5-d89b-5b3f-d6374601ca71", + "OpaqueRef:507ee5fc-59d3-e635-21d5-98a5cace4bf2", + "OpaqueRef:6c9b814c-e1c2-b8be-198f-de358686b10a", + "OpaqueRef:a17e721d-faf4-6ad1-c617-dd4899279534", + "OpaqueRef:6ac77a0f-f079-8067-85cc-c9ae2f8dcca9", + "OpaqueRef:f61edc83-91d9-a161-113f-00c110196238", + "OpaqueRef:b71938bf-4c4f-eb17-7e78-588e71297a74", + "OpaqueRef:01befb95-412e-e9dd-5b5d-edd50df61cb1", + "OpaqueRef:a3f9481e-fe3d-1f00-235f-44d404f51128", + "OpaqueRef:0760c608-b02e-743a-18a1-fa8f205374d6", + "OpaqueRef:204558d7-dce0-2304-bdc5-80ec5fd7e3c3", + "OpaqueRef:9eccc765-9726-d220-96b1-2e85adf77ecc", + "OpaqueRef:91cfa47b-52f9-a4e3-4e78-52e3eb3e5141", + "OpaqueRef:3fffd7c7-f4d1-6b03-a5b8-d75211bb7b8f", + "OpaqueRef:7efce157-9b93-d116-f3f8-7eb0c6fb1a79", + "OpaqueRef:e2209ae9-5362-3a20-f691-9294144e49f2", + "OpaqueRef:1ced32ca-fec4-8b44-0e8f-753c97f2d93f", + "OpaqueRef:65b14ae7-f440-0c4d-4af9-c7946b90fd2f" + ], + "updates_requiring_reboot": [], + "uuid": "dff6702e-bcb6-4704-8dd4-952e8c883365", + "virtual_hardware_platform_versions": [ + "0", + "1", + "2" + ] + } + }, + "network": { + "OpaqueRef:8a404c5e-5673-ab69-5d6f-5a35a33b8724": { + "MTU": "1500", + "PIFs": [], + "VIFs": [], + "allowed_operations": [], + "assigned_ips": { + "OpaqueRef:8171dad1-f902-ec00-7ba2-9f92d8aa75ab": "169.254.0.3", + "OpaqueRef:9754a0ed-e100-d224-6a70-a55a9c2cedf9": "169.254.0.2" + }, + "blobs": {}, + "bridge": "xenapi", + "current_operations": {}, + "default_locking_mode": "unlocked", + "managed": true, + "name_description": "Network on which guests will be assigned a private link-local IP address which can be used to talk XenAPI", + "name_label": "Host internal management network", + "other_config": { + "ip_begin": "169.254.0.1", + "ip_end": "169.254.255.254", + "is_guest_installer_network": "true", + "is_host_internal_management_network": "true", + "netmask": "255.255.0.0" + }, + "tags": [], + "uuid": "dbb96525-944f-0d1a-54ed-e65cb6d07450" + } + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json.license b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-2-params.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json new file mode 100644 index 000000000..5ed7df7f1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json @@ -0,0 +1,75 @@ +{ + "cdrom": { + "type": "none" + }, + "customization_agent": "custom", + "disks": [ + { + "name": "ansible-test-vm-3-root", + "name_desc": "/", + "os_device": "xvda", + "size": 8589934592, + "sr": "Ansible Test Storage 1", + "sr_uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "vbd_userdevice": "0" + } + ], + "domid": "-1", + "folder": "", + "hardware": { + "memory_mb": 1024, + "num_cpu_cores_per_socket": 1, + "num_cpus": 1 + }, + "home_server": "", + "is_template": false, + "name": "ansible-test-vm-3", + "name_desc": "Created by Ansible", + "networks": [ + { + "gateway": "", + "gateway6": "", + "ip": "169.254.0.3", + "ip6": [], + "mac": "72:fb:c7:ac:b9:97", + "mtu": "1500", + "name": "Host internal management network", + "netmask": "", + "prefix": "", + "prefix6": "", + "vif_device": "0" + } + ], + "other_config": { + "auto_poweron": "true", + "base_template_name": "zatemplate", + "import_task": "OpaqueRef:9948fd82-6d79-8882-2f01-4edc8795e361", + "install-methods": "cdrom,nfs,http,ftp", + "install-repository": "http://mirror.centos.org/centos-6/6.2/os/x86_64/", + "instant": "true", + "last_shutdown_action": "Destroy", + "last_shutdown_initiator": "external", + "last_shutdown_reason": "halted", + "last_shutdown_time": "20140314T21:16:41Z", + "linux_template": "true", + "mac_seed": "06e27068-70c2-4c69-614b-7c54b5a4a781", + "rhel6": "true" + }, + "platform": { + "acpi": "true", + "apic": "true", + "cores-per-socket": "1", + "nx": "false", + "pae": "true", + "viridian": "true" + }, + "state": "poweredoff", + "uuid": "8f5bc97c-42fa-d619-aba4-d25eced735e0", + "xenstore_data": { + "vm-data": "", + "vm-data/networks": "", + "vm-data/networks/0": "", + "vm-data/networks/0/mac": "72:fb:c7:ac:b9:97", + "vm-data/networks/0/name": "Host internal management network" + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json.license b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-facts.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json new file mode 100644 index 000000000..02e224bf0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json @@ -0,0 +1,420 @@ +{ + "SR": { + "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f": { + "PBDs": [], + "VDIs": [], + "allowed_operations": [ + "unplug", + "plug", + "pbd_create", + "update", + "pbd_destroy", + "vdi_resize", + "vdi_clone", + "scan", + "vdi_snapshot", + "vdi_mirror", + "vdi_create", + "vdi_destroy" + ], + "blobs": {}, + "clustered": false, + "content_type": "", + "current_operations": {}, + "introduced_by": "OpaqueRef:NULL", + "is_tools_sr": false, + "local_cache_enabled": false, + "name_description": "", + "name_label": "Ansible Test Storage 1", + "other_config": { + "auto-scan": "false" + }, + "physical_size": "2521133219840", + "physical_utilisation": "1551485632512", + "shared": true, + "sm_config": { + "allocation": "thick", + "devserial": "scsi-3600a098038302d353624495242443848", + "multipathable": "true", + "use_vhd": "true" + }, + "tags": [], + "type": "lvmohba", + "uuid": "767b30e4-f8db-a83d-8ba7-f5e6e732e06f", + "virtual_allocation": "1556925644800" + } + }, + "VBD": { + "OpaqueRef:024b722e-8d0f-65e6-359e-f301a009b683": { + "VDI": "OpaqueRef:NULL", + "VM": "OpaqueRef:957f576a-2347-1789-80db-4beb50466bc2", + "allowed_operations": [ + "attach", + "insert" + ], + "bootable": false, + "current_operations": {}, + "currently_attached": false, + "device": "", + "empty": true, + "metrics": "OpaqueRef:81509584-b22f-bc71-3c4e-e6c3bdca71f0", + "mode": "RO", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "CD", + "unpluggable": true, + "userdevice": "3", + "uuid": "38d850d0-c402-490e-6b97-1d23558c4e0e" + }, + "OpaqueRef:235f4f04-1dc9-9fa5-c229-a1df187ba48c": { + "VDI": "OpaqueRef:4d3e9fc7-ae61-b312-e0a8-b53bee06282e", + "VM": "OpaqueRef:957f576a-2347-1789-80db-4beb50466bc2", + "allowed_operations": [ + "attach" + ], + "bootable": true, + "current_operations": {}, + "currently_attached": false, + "device": "xvda", + "empty": false, + "metrics": "OpaqueRef:529f6071-5627-28c5-1f41-ee8c0733f1da", + "mode": "RW", + "other_config": { + "owner": "" + }, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "storage_lock": false, + "type": "Disk", + "unpluggable": false, + "userdevice": "0", + "uuid": "3fd7d35c-cb9d-f0c4-726b-e188ef0dc446" + } + }, + "VDI": { + "OpaqueRef:4d3e9fc7-ae61-b312-e0a8-b53bee06282e": { + "SR": "OpaqueRef:f746e964-e0fe-c36d-d60b-6897cfde583f", + "VBDs": [ + "OpaqueRef:235f4f04-1dc9-9fa5-c229-a1df187ba48c" + ], + "allow_caching": false, + "allowed_operations": [ + "forget", + "generate_config", + "update", + "resize", + "destroy", + "clone", + "copy", + "snapshot" + ], + "crash_dumps": [], + "current_operations": {}, + "is_a_snapshot": false, + "is_tools_iso": false, + "location": "bdd0baeb-5447-4963-9e71-a5ff6e85fa59", + "managed": true, + "metadata_latest": false, + "metadata_of_pool": "", + "missing": false, + "name_description": "/", + "name_label": "ansible-test-vm-3-root", + "on_boot": "persist", + "other_config": { + "content_id": "cd8e8b2b-f158-c519-02f0-81d130fe83c5" + }, + "parent": "OpaqueRef:NULL", + "physical_utilisation": "8615100416", + "read_only": false, + "sharable": false, + "sm_config": { + "vdi_type": "vhd" + }, + "snapshot_of": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "storage_lock": false, + "tags": [], + "type": "system", + "uuid": "bdd0baeb-5447-4963-9e71-a5ff6e85fa59", + "virtual_size": "8589934592", + "xenstore_data": {} + } + }, + "VIF": { + "OpaqueRef:8171dad1-f902-ec00-7ba2-9f92d8aa75ab": { + "MAC": "72:fb:c7:ac:b9:97", + "MAC_autogenerated": true, + "MTU": "1500", + "VM": "OpaqueRef:957f576a-2347-1789-80db-4beb50466bc2", + "allowed_operations": [ + "attach" + ], + "current_operations": {}, + "currently_attached": false, + "device": "0", + "ipv4_addresses": [], + "ipv4_allowed": [], + "ipv4_configuration_mode": "None", + "ipv4_gateway": "", + "ipv6_addresses": [], + "ipv6_allowed": [], + "ipv6_configuration_mode": "None", + "ipv6_gateway": "", + "locking_mode": "network_default", + "metrics": "OpaqueRef:e5b53fb1-3e99-4bf5-6b00-95fdba1f2610", + "network": "OpaqueRef:8a404c5e-5673-ab69-5d6f-5a35a33b8724", + "other_config": {}, + "qos_algorithm_params": {}, + "qos_algorithm_type": "", + "qos_supported_algorithms": [], + "runtime_properties": {}, + "status_code": "0", + "status_detail": "", + "uuid": "94bd4913-4940-437c-a1c3-50f7eb354c55" + } + }, + "VM": { + "OpaqueRef:957f576a-2347-1789-80db-4beb50466bc2": { + "HVM_boot_params": { + "order": "" + }, + "HVM_boot_policy": "", + "HVM_shadow_multiplier": 1.0, + "PCI_bus": "", + "PV_args": "graphical utf8", + "PV_bootloader": "pygrub", + "PV_bootloader_args": "", + "PV_kernel": "", + "PV_legacy_args": "", + "PV_ramdisk": "", + "VBDs": [ + "OpaqueRef:235f4f04-1dc9-9fa5-c229-a1df187ba48c", + "OpaqueRef:024b722e-8d0f-65e6-359e-f301a009b683" + ], + "VCPUs_at_startup": "1", + "VCPUs_max": "1", + "VCPUs_params": {}, + "VGPUs": [], + "VIFs": [ + "OpaqueRef:8171dad1-f902-ec00-7ba2-9f92d8aa75ab" + ], + "VTPMs": [], + "actions_after_crash": "restart", + "actions_after_reboot": "restart", + "actions_after_shutdown": "destroy", + "affinity": "OpaqueRef:NULL", + "allowed_operations": [ + "changing_dynamic_range", + "changing_shadow_memory", + "changing_static_range", + "make_into_template", + "migrate_send", + "destroy", + "export", + "start_on", + "start", + "clone", + "copy", + "snapshot" + ], + "appliance": "OpaqueRef:NULL", + "attached_PCIs": [], + "bios_strings": { + "bios-vendor": "Xen", + "bios-version": "", + "hp-rombios": "", + "oem-1": "Xen", + "oem-2": "MS_VM_CERT/SHA1/bdbeb6e0a816d43fa6d3fe8aaef04c2bad9d3e3d", + "system-manufacturer": "Xen", + "system-product-name": "HVM domU", + "system-serial-number": "", + "system-version": "" + }, + "blobs": {}, + "blocked_operations": {}, + "children": [], + "consoles": [], + "crash_dumps": [], + "current_operations": {}, + "domarch": "", + "domid": "-1", + "generation_id": "", + "guest_metrics": "OpaqueRef:6a8acd85-4cab-4e52-27d5-5f4a51c1bf69", + "ha_always_run": false, + "ha_restart_priority": "", + "hardware_platform_version": "0", + "has_vendor_device": false, + "is_a_snapshot": false, + "is_a_template": false, + "is_control_domain": false, + "is_default_template": false, + "is_snapshot_from_vmpp": false, + "is_vmss_snapshot": false, + "last_boot_CPU_flags": { + "features": "17c9cbf5-f6f83203-2191cbf5-00000023-00000001-00000329-00000000-00000000-00001000-0c000000", + "vendor": "GenuineIntel" + }, + "last_booted_record": "", + "memory_dynamic_max": "1073741824", + "memory_dynamic_min": "1073741824", + "memory_overhead": "10485760", + "memory_static_max": "1073741824", + "memory_static_min": "536870912", + "memory_target": "0", + "metrics": "OpaqueRef:87fc5829-478b-1dcd-989f-50e8ba58a87d", + "name_description": "Created by Ansible", + "name_label": "ansible-test-vm-3", + "order": "0", + "other_config": { + "auto_poweron": "true", + "base_template_name": "zatemplate", + "import_task": "OpaqueRef:9948fd82-6d79-8882-2f01-4edc8795e361", + "install-methods": "cdrom,nfs,http,ftp", + "install-repository": "http://mirror.centos.org/centos-6/6.2/os/x86_64/", + "instant": "true", + "last_shutdown_action": "Destroy", + "last_shutdown_initiator": "external", + "last_shutdown_reason": "halted", + "last_shutdown_time": "20140314T21:16:41Z", + "linux_template": "true", + "mac_seed": "06e27068-70c2-4c69-614b-7c54b5a4a781", + "rhel6": "true" + }, + "parent": "OpaqueRef:NULL", + "platform": { + "acpi": "true", + "apic": "true", + "cores-per-socket": "1", + "nx": "false", + "pae": "true", + "viridian": "true" + }, + "power_state": "Halted", + "protection_policy": "OpaqueRef:NULL", + "recommendations": "", + "reference_label": "", + "requires_reboot": false, + "resident_on": "OpaqueRef:NULL", + "shutdown_delay": "0", + "snapshot_info": {}, + "snapshot_metadata": "", + "snapshot_of": "OpaqueRef:NULL", + "snapshot_schedule": "OpaqueRef:NULL", + "snapshot_time": "19700101T00:00:00Z", + "snapshots": [], + "start_delay": "0", + "suspend_SR": "OpaqueRef:NULL", + "suspend_VDI": "OpaqueRef:NULL", + "tags": [ + "web-frontend" + ], + "transportable_snapshot_id": "", + "user_version": "1", + "uuid": "8f5bc97c-42fa-d619-aba4-d25eced735e0", + "version": "0", + "xenstore_data": { + "vm-data": "", + "vm-data/networks": "", + "vm-data/networks/0": "", + "vm-data/networks/0/mac": "72:fb:c7:ac:b9:97", + "vm-data/networks/0/name": "Host internal management network" + } + } + }, + "VM_guest_metrics": { + "OpaqueRef:6a8acd85-4cab-4e52-27d5-5f4a51c1bf69": { + "PV_drivers_detected": true, + "PV_drivers_up_to_date": true, + "PV_drivers_version": { + "build": "46676", + "major": "5", + "micro": "100", + "minor": "6" + }, + "can_use_hotplug_vbd": "unspecified", + "can_use_hotplug_vif": "unspecified", + "disks": {}, + "last_updated": "20190113T19:36:07Z", + "live": true, + "memory": {}, + "networks": { + "0/ip": "169.254.0.3" + }, + "os_version": { + "distro": "centos", + "major": "6", + "minor": "10", + "name": "CentOS release 6.10 (Final)", + "uname": "2.6.32-754.6.3.el6.x86_64" + }, + "other": { + "feature-balloon": "1", + "has-vendor-device": "0", + "platform-feature-multiprocessor-suspend": "1" + }, + "other_config": {}, + "uuid": "3928a6a4-1acd-c134-ed35-eb0ccfaed65c" + } + }, + "VM_metrics": { + "OpaqueRef:87fc5829-478b-1dcd-989f-50e8ba58a87d": { + "VCPUs_CPU": {}, + "VCPUs_flags": {}, + "VCPUs_number": "0", + "VCPUs_params": {}, + "VCPUs_utilisation": { + "0": 0.0 + }, + "hvm": false, + "install_time": "20190113T19:35:05Z", + "last_updated": "19700101T00:00:00Z", + "memory_actual": "1073741824", + "nested_virt": false, + "nomigrate": false, + "other_config": {}, + "start_time": "19700101T00:00:00Z", + "state": [], + "uuid": "6cb05fe9-b83e-34c8-29e0-3b793e1da661" + } + }, + "host": {}, + "network": { + "OpaqueRef:8a404c5e-5673-ab69-5d6f-5a35a33b8724": { + "MTU": "1500", + "PIFs": [], + "VIFs": [], + "allowed_operations": [], + "assigned_ips": { + "OpaqueRef:8171dad1-f902-ec00-7ba2-9f92d8aa75ab": "169.254.0.3", + "OpaqueRef:9754a0ed-e100-d224-6a70-a55a9c2cedf9": "169.254.0.2" + }, + "blobs": {}, + "bridge": "xenapi", + "current_operations": {}, + "default_locking_mode": "unlocked", + "managed": true, + "name_description": "Network on which guests will be assigned a private link-local IP address which can be used to talk XenAPI", + "name_label": "Host internal management network", + "other_config": { + "ip_begin": "169.254.0.1", + "ip_end": "169.254.255.254", + "is_guest_installer_network": "true", + "is_host_internal_management_network": "true", + "netmask": "255.255.0.0" + }, + "tags": [], + "uuid": "dbb96525-944f-0d1a-54ed-e65cb6d07450" + } + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json.license b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/fixtures/ansible-test-vm-3-params.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_gather_vm_params_and_facts.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_gather_vm_params_and_facts.py new file mode 100644 index 000000000..37e54b2b5 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_gather_vm_params_and_facts.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +from .common import testcase_bad_xenapi_refs + + +testcase_gather_vm_params_and_facts = { + "params": [ + ["ansible-test-vm-1-params.json", "ansible-test-vm-1-facts.json"], + ["ansible-test-vm-2-params.json", "ansible-test-vm-2-facts.json"], + ["ansible-test-vm-3-params.json", "ansible-test-vm-3-facts.json"], + ], + "ids": [ + "ansible-test-vm-1", + "ansible-test-vm-2", + "ansible-test-vm-3", + ], +} + + +@pytest.mark.parametrize('vm_ref', testcase_bad_xenapi_refs['params'], ids=testcase_bad_xenapi_refs['ids']) +def test_gather_vm_params_bad_vm_ref(fake_ansible_module, xenserver, vm_ref): + """Tests return of empty dict on bad vm_ref.""" + assert xenserver.gather_vm_params(fake_ansible_module, vm_ref) == {} + + +def test_gather_vm_facts_no_vm_params(fake_ansible_module, xenserver): + """Tests return of empty facts dict when vm_params is not available""" + assert xenserver.gather_vm_facts(fake_ansible_module, None) == {} + assert xenserver.gather_vm_facts(fake_ansible_module, {}) == {} + + +@pytest.mark.parametrize('fixture_data_from_file', + testcase_gather_vm_params_and_facts['params'], + ids=testcase_gather_vm_params_and_facts['ids'], + indirect=True) +def test_gather_vm_params_and_facts(mocker, fake_ansible_module, XenAPI, xenserver, fixture_data_from_file): + """Tests proper parsing of VM parameters and facts.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + if "params" in list(fixture_data_from_file.keys())[0]: + params_file = list(fixture_data_from_file.keys())[0] + facts_file = list(fixture_data_from_file.keys())[1] + else: + params_file = list(fixture_data_from_file.keys())[1] + facts_file = list(fixture_data_from_file.keys())[0] + + mocked_returns = { + "VM.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['VM'][obj_ref], + "VM_metrics.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['VM_metrics'][obj_ref], + "VM_guest_metrics.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['VM_guest_metrics'][obj_ref], + "VBD.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['VBD'][obj_ref], + "VDI.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['VDI'][obj_ref], + "SR.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['SR'][obj_ref], + "VIF.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['VIF'][obj_ref], + "network.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['network'][obj_ref], + "host.get_record.side_effect": lambda obj_ref: fixture_data_from_file[params_file]['host'][obj_ref], + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.get_xenserver_version', return_value=[7, 2, 0]) + + vm_ref = list(fixture_data_from_file[params_file]['VM'].keys())[0] + + assert xenserver.gather_vm_facts(fake_ansible_module, xenserver.gather_vm_params(fake_ansible_module, vm_ref)) == fixture_data_from_file[facts_file] diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_get_object_ref.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_get_object_ref.py new file mode 100644 index 000000000..242e1debd --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_get_object_ref.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +from .FakeAnsibleModule import FailJsonException +from .common import fake_xenapi_ref + + +def test_get_object_ref_xenapi_failure(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests catching of XenAPI failures.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi_request', side_effect=XenAPI.Failure('Fake XAPI method call error!')) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.get_object_ref(fake_ansible_module, "name") + + assert exc_info.value.kwargs['msg'] == "XAPI ERROR: Fake XAPI method call error!" + + +def test_get_object_ref_bad_uuid_and_name(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests failure on bad object uuid and/or name.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi_request') + + with pytest.raises(FailJsonException) as exc_info: + xenserver.get_object_ref(fake_ansible_module, None, msg_prefix="Test: ") + + mocked_xenapi.xenapi_request.assert_not_called() + assert exc_info.value.kwargs['msg'] == "Test: no valid name or UUID supplied for VM!" + + +def test_get_object_ref_uuid_not_found(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests when object is not found by uuid.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi_request', side_effect=XenAPI.Failure('Fake XAPI not found error!')) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.get_object_ref(fake_ansible_module, "name", uuid="fake-uuid", msg_prefix="Test: ") + + assert exc_info.value.kwargs['msg'] == "Test: VM with UUID 'fake-uuid' not found!" + assert xenserver.get_object_ref(fake_ansible_module, "name", uuid="fake-uuid", fail=False, msg_prefix="Test: ") is None + + +def test_get_object_ref_name_not_found(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests when object is not found by name.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi_request', return_value=[]) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.get_object_ref(fake_ansible_module, "name", msg_prefix="Test: ") + + assert exc_info.value.kwargs['msg'] == "Test: VM with name 'name' not found!" + assert xenserver.get_object_ref(fake_ansible_module, "name", fail=False, msg_prefix="Test: ") is None + + +def test_get_object_ref_name_multiple_found(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests when multiple objects are found by name.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi_request', return_value=[fake_xenapi_ref('VM'), fake_xenapi_ref('VM')]) + + error_msg = "Test: multiple VMs with name 'name' found! Please use UUID." + + with pytest.raises(FailJsonException) as exc_info: + xenserver.get_object_ref(fake_ansible_module, "name", msg_prefix="Test: ") + + assert exc_info.value.kwargs['msg'] == error_msg + + with pytest.raises(FailJsonException) as exc_info: + xenserver.get_object_ref(fake_ansible_module, "name", fail=False, msg_prefix="Test: ") + + assert exc_info.value.kwargs['msg'] == error_msg diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_misc.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_misc.py new file mode 100644 index 000000000..b22e4aa35 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_misc.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + + +def test_xapi_to_module_vm_power_state_bad_power_state(xenserver): + """Tests that None is returned on bad power state.""" + assert xenserver.xapi_to_module_vm_power_state("bad") is None + + +def test_module_to_xapi_vm_power_state_bad_power_state(xenserver): + """Tests that None is returned on bad power state.""" + assert xenserver.module_to_xapi_vm_power_state("bad") is None diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_netaddr_functions.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_netaddr_functions.py new file mode 100644 index 000000000..d072ce207 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_netaddr_functions.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +from ansible.module_utils.common.network import is_mac + +testcase_is_valid_mac_addr = [ + ('A4-23-8D-F8-C9-E5', True), + ('35:71:F4:11:0B:D8', True), + ('b3-bd-20-59-0c-cf', True), + ('32:61:ca:65:f1:f4', True), + ('asdf', False), + ('A4-23-8D-G8-C9-E5', False), + ('A4-3-8D-F8-C9-E5', False), + ('A4-23-88D-F8-C9-E5', False), + ('A4-23-8D-F8-C9_E5', False), + ('A4-23--8D-F8-C9-E5', False), +] + +testcase_is_valid_ip_addr = [ + ('0.0.0.0', True), + ('10.0.0.1', True), + ('192.168.0.1', True), + ('255.255.255.255', True), + ('asdf', False), + ('a.b.c.d', False), + ('345.345.345.345', False), + ('-10.0.0.1', False), +] + +testcase_is_valid_ip_netmask = [ + ('240.0.0.0', True), + ('255.224.0.0', True), + ('255.255.248.0', True), + ('255.255.255.255', True), + ('asdf', False), + ('a.b.c.d', False), + ('192.168.0.1', False), + ('255.0.248.0', False), +] + +testcase_is_valid_ip_prefix = [ + ('0', True), + ('16', True), + ('24', True), + ('32', True), + ('asdf', False), + ('-10', False), + ('60', False), + ('60s', False), +] + +testcase_ip_prefix_to_netmask = { + "params": [ + ('0', '0.0.0.0'), + ('8', '255.0.0.0'), + ('11', '255.224.0.0'), + ('16', '255.255.0.0'), + ('21', '255.255.248.0'), + ('24', '255.255.255.0'), + ('26', '255.255.255.192'), + ('32', '255.255.255.255'), + ('a', ''), + ('60', ''), + ], + "ids": [ + '0', + '8', + '11', + '16', + '21', + '24', + '26', + '32', + 'a', + '60', + ], +} + +testcase_ip_netmask_to_prefix = { + "params": [ + ('0.0.0.0', '0'), + ('255.0.0.0', '8'), + ('255.224.0.0', '11'), + ('255.255.0.0', '16'), + ('255.255.248.0', '21'), + ('255.255.255.0', '24'), + ('255.255.255.192', '26'), + ('255.255.255.255', '32'), + ('a', ''), + ('60', ''), + ], + "ids": [ + '0.0.0.0', + '255.0.0.0', + '255.224.0.0', + '255.255.0.0', + '255.255.248.0', + '255.255.255.0', + '255.255.255.192', + '255.255.255.255', + 'a', + '60', + ], +} + +testcase_is_valid_ip6_addr = [ + ('::1', True), + ('2001:DB8:0:0:8:800:200C:417A', True), + ('2001:DB8::8:800:200C:417A', True), + ('FF01::101', True), + ('asdf', False), + ('2001:DB8:0:0:8:800:200C:417A:221', False), + ('FF01::101::2', False), + ('2001:db8:85a3::8a2e:370k:7334', False), +] + +testcase_is_valid_ip6_prefix = [ + ('0', True), + ('56', True), + ('78', True), + ('128', True), + ('asdf', False), + ('-10', False), + ('345', False), + ('60s', False), +] + + +@pytest.mark.parametrize('mac_addr, result', testcase_is_valid_mac_addr) +def test_is_valid_mac_addr(xenserver, mac_addr, result): + """Tests against examples of valid and invalid mac addresses.""" + assert is_mac(mac_addr) is result + + +@pytest.mark.parametrize('ip_addr, result', testcase_is_valid_ip_addr) +def test_is_valid_ip_addr(xenserver, ip_addr, result): + """Tests against examples of valid and invalid ip addresses.""" + assert xenserver.is_valid_ip_addr(ip_addr) is result + + +@pytest.mark.parametrize('ip_netmask, result', testcase_is_valid_ip_netmask) +def test_is_valid_ip_netmask(xenserver, ip_netmask, result): + """Tests against examples of valid and invalid ip netmasks.""" + assert xenserver.is_valid_ip_netmask(ip_netmask) is result + + +@pytest.mark.parametrize('ip_prefix, result', testcase_is_valid_ip_prefix) +def test_is_valid_ip_prefix(xenserver, ip_prefix, result): + """Tests against examples of valid and invalid ip prefixes.""" + assert xenserver.is_valid_ip_prefix(ip_prefix) is result + + +@pytest.mark.parametrize('ip_prefix, ip_netmask', testcase_ip_prefix_to_netmask['params'], ids=testcase_ip_prefix_to_netmask['ids']) +def test_ip_prefix_to_netmask(xenserver, ip_prefix, ip_netmask): + """Tests ip prefix to netmask conversion.""" + assert xenserver.ip_prefix_to_netmask(ip_prefix) == ip_netmask + + +@pytest.mark.parametrize('ip_netmask, ip_prefix', testcase_ip_netmask_to_prefix['params'], ids=testcase_ip_netmask_to_prefix['ids']) +def test_ip_netmask_to_prefix(xenserver, ip_netmask, ip_prefix): + """Tests ip netmask to prefix conversion.""" + assert xenserver.ip_netmask_to_prefix(ip_netmask) == ip_prefix + + +@pytest.mark.parametrize('ip6_addr, result', testcase_is_valid_ip6_addr) +def test_is_valid_ip6_addr(xenserver, ip6_addr, result): + """Tests against examples of valid and invalid ip6 addresses.""" + assert xenserver.is_valid_ip6_addr(ip6_addr) is result + + +@pytest.mark.parametrize('ip6_prefix, result', testcase_is_valid_ip6_prefix) +def test_is_valid_ip6_prefix(xenserver, ip6_prefix, result): + """Tests against examples of valid and invalid ip6 prefixes.""" + assert xenserver.is_valid_ip6_prefix(ip6_prefix) is result diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_set_vm_power_state.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_set_vm_power_state.py new file mode 100644 index 000000000..279dc9912 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_set_vm_power_state.py @@ -0,0 +1,414 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +from .FakeAnsibleModule import FailJsonException +from .common import fake_xenapi_ref, testcase_bad_xenapi_refs + + +testcase_set_vm_power_state_bad_transitions = { + "params": [ + ('restarted', 'Halted', "Cannot restart VM in state 'poweredoff'!"), + ('restarted', 'Suspended', "Cannot restart VM in state 'suspended'!"), + ('suspended', 'Halted', "Cannot suspend VM in state 'poweredoff'!"), + ('suspended', 'Paused', "Cannot suspend VM in state 'paused'!"), + ('shutdownguest', 'Halted', "Cannot shutdown guest when VM is in state 'poweredoff'!"), + ('shutdownguest', 'Suspended', "Cannot shutdown guest when VM is in state 'suspended'!"), + ('shutdownguest', 'Paused', "Cannot shutdown guest when VM is in state 'paused'!"), + ('rebootguest', 'Halted', "Cannot reboot guest when VM is in state 'poweredoff'!"), + ('rebootguest', 'Suspended', "Cannot reboot guest when VM is in state 'suspended'!"), + ('rebootguest', 'Paused', "Cannot reboot guest when VM is in state 'paused'!"), + ], + "ids": [ + "poweredoff->restarted", + "suspended->restarted", + "poweredoff->suspended", + "paused->suspended", + "poweredoff->shutdownguest", + "suspended->shutdownguest", + "paused->shutdownguest", + "poweredoff->rebootguest", + "suspended->rebootguest", + "paused->rebootguest", + ], +} + +testcase_set_vm_power_state_task_timeout = { + "params": [ + ('shutdownguest', "Guest shutdown task failed: 'timeout'!"), + ('rebootguest', "Guest reboot task failed: 'timeout'!"), + ], + "ids": [ + "shutdownguest-timeout", + "rebootguest-timeout", + ], +} + +testcase_set_vm_power_state_no_transitions = { + "params": [ + ('poweredon', "Running"), + ('Poweredon', "Running"), + ('powered-on', "Running"), + ('Powered_on', "Running"), + ('poweredoff', "Halted"), + ('Poweredoff', "Halted"), + ('powered-off', "Halted"), + ('powered_off', "Halted"), + ('suspended', "Suspended"), + ('Suspended', "Suspended"), + ], + "ids": [ + "poweredon", + "poweredon-cap", + "poweredon-dash", + "poweredon-under", + "poweredoff", + "poweredoff-cap", + "poweredoff-dash", + "poweredoff-under", + "suspended", + "suspended-cap", + ], +} + +testcase_set_vm_power_state_transitions = { + "params": [ + ('poweredon', 'Halted', 'running', 'VM.start'), + ('Poweredon', 'Halted', 'running', 'VM.start'), + ('powered-on', 'Halted', 'running', 'VM.start'), + ('Powered_on', 'Halted', 'running', 'VM.start'), + ('poweredon', 'Suspended', 'running', 'VM.resume'), + ('Poweredon', 'Suspended', 'running', 'VM.resume'), + ('powered-on', 'Suspended', 'running', 'VM.resume'), + ('Powered_on', 'Suspended', 'running', 'VM.resume'), + ('poweredon', 'Paused', 'running', 'VM.unpause'), + ('Poweredon', 'Paused', 'running', 'VM.unpause'), + ('powered-on', 'Paused', 'running', 'VM.unpause'), + ('Powered_on', 'Paused', 'running', 'VM.unpause'), + ('poweredoff', 'Running', 'halted', 'VM.hard_shutdown'), + ('Poweredoff', 'Running', 'halted', 'VM.hard_shutdown'), + ('powered-off', 'Running', 'halted', 'VM.hard_shutdown'), + ('powered_off', 'Running', 'halted', 'VM.hard_shutdown'), + ('poweredoff', 'Suspended', 'halted', 'VM.hard_shutdown'), + ('Poweredoff', 'Suspended', 'halted', 'VM.hard_shutdown'), + ('powered-off', 'Suspended', 'halted', 'VM.hard_shutdown'), + ('powered_off', 'Suspended', 'halted', 'VM.hard_shutdown'), + ('poweredoff', 'Paused', 'halted', 'VM.hard_shutdown'), + ('Poweredoff', 'Paused', 'halted', 'VM.hard_shutdown'), + ('powered-off', 'Paused', 'halted', 'VM.hard_shutdown'), + ('powered_off', 'Paused', 'halted', 'VM.hard_shutdown'), + ('restarted', 'Running', 'running', 'VM.hard_reboot'), + ('Restarted', 'Running', 'running', 'VM.hard_reboot'), + ('restarted', 'Paused', 'running', 'VM.hard_reboot'), + ('Restarted', 'Paused', 'running', 'VM.hard_reboot'), + ('suspended', 'Running', 'suspended', 'VM.suspend'), + ('Suspended', 'Running', 'suspended', 'VM.suspend'), + ('shutdownguest', 'Running', 'halted', 'VM.clean_shutdown'), + ('Shutdownguest', 'Running', 'halted', 'VM.clean_shutdown'), + ('shutdown-guest', 'Running', 'halted', 'VM.clean_shutdown'), + ('shutdown_guest', 'Running', 'halted', 'VM.clean_shutdown'), + ('rebootguest', 'Running', 'running', 'VM.clean_reboot'), + ('rebootguest', 'Running', 'running', 'VM.clean_reboot'), + ('reboot-guest', 'Running', 'running', 'VM.clean_reboot'), + ('reboot_guest', 'Running', 'running', 'VM.clean_reboot'), + ], + "ids": [ + "poweredoff->poweredon", + "poweredoff->poweredon-cap", + "poweredoff->poweredon-dash", + "poweredoff->poweredon-under", + "suspended->poweredon", + "suspended->poweredon-cap", + "suspended->poweredon-dash", + "suspended->poweredon-under", + "paused->poweredon", + "paused->poweredon-cap", + "paused->poweredon-dash", + "paused->poweredon-under", + "poweredon->poweredoff", + "poweredon->poweredoff-cap", + "poweredon->poweredoff-dash", + "poweredon->poweredoff-under", + "suspended->poweredoff", + "suspended->poweredoff-cap", + "suspended->poweredoff-dash", + "suspended->poweredoff-under", + "paused->poweredoff", + "paused->poweredoff-cap", + "paused->poweredoff-dash", + "paused->poweredoff-under", + "poweredon->restarted", + "poweredon->restarted-cap", + "paused->restarted", + "paused->restarted-cap", + "poweredon->suspended", + "poweredon->suspended-cap", + "poweredon->shutdownguest", + "poweredon->shutdownguest-cap", + "poweredon->shutdownguest-dash", + "poweredon->shutdownguest-under", + "poweredon->rebootguest", + "poweredon->rebootguest-cap", + "poweredon->rebootguest-dash", + "poweredon->rebootguest-under", + ], +} + +testcase_set_vm_power_state_transitions_async = { + "params": [ + ('shutdownguest', 'Running', 'halted', 'Async.VM.clean_shutdown'), + ('Shutdownguest', 'Running', 'halted', 'Async.VM.clean_shutdown'), + ('shutdown-guest', 'Running', 'halted', 'Async.VM.clean_shutdown'), + ('shutdown_guest', 'Running', 'halted', 'Async.VM.clean_shutdown'), + ('rebootguest', 'Running', 'running', 'Async.VM.clean_reboot'), + ('rebootguest', 'Running', 'running', 'Async.VM.clean_reboot'), + ('reboot-guest', 'Running', 'running', 'Async.VM.clean_reboot'), + ('reboot_guest', 'Running', 'running', 'Async.VM.clean_reboot'), + ], + "ids": [ + "poweredon->shutdownguest", + "poweredon->shutdownguest-cap", + "poweredon->shutdownguest-dash", + "poweredon->shutdownguest-under", + "poweredon->rebootguest", + "poweredon->rebootguest-cap", + "poweredon->rebootguest-dash", + "poweredon->rebootguest-under", + ], +} + + +@pytest.mark.parametrize('vm_ref', testcase_bad_xenapi_refs['params'], ids=testcase_bad_xenapi_refs['ids']) +def test_set_vm_power_state_bad_vm_ref(fake_ansible_module, xenserver, vm_ref): + """Tests failure on bad vm_ref.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.set_vm_power_state(fake_ansible_module, vm_ref, None) + + assert exc_info.value.kwargs['msg'] == "Cannot set VM power state. Invalid VM reference supplied!" + + +def test_set_vm_power_state_xenapi_failure(mock_xenapi_failure, fake_ansible_module, xenserver): + """Tests catching of XenAPI failures.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), "poweredon") + + assert exc_info.value.kwargs['msg'] == "XAPI ERROR: %s" % mock_xenapi_failure[1] + + +def test_set_vm_power_state_bad_power_state(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests failure on unsupported power state.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": "Running", + } + + mocked_xenapi.configure_mock(**mocked_returns) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), "bad") + + # Beside VM.get_power_state() no other method should have been + # called additionally. + assert len(mocked_xenapi.method_calls) == 1 + + assert exc_info.value.kwargs['msg'] == "Requested VM power state 'bad' is unsupported!" + + +@pytest.mark.parametrize('power_state_desired, power_state_current, error_msg', + testcase_set_vm_power_state_bad_transitions['params'], + ids=testcase_set_vm_power_state_bad_transitions['ids']) +def test_set_vm_power_state_bad_transition(mocker, fake_ansible_module, XenAPI, xenserver, power_state_desired, power_state_current, error_msg): + """Tests failure on bad power state transition.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": power_state_current, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), power_state_desired) + + # Beside VM.get_power_state() no other method should have been + # called additionally. + assert len(mocked_xenapi.method_calls) == 1 + + assert exc_info.value.kwargs['msg'] == error_msg + + +@pytest.mark.parametrize('power_state, error_msg', + testcase_set_vm_power_state_task_timeout['params'], + ids=testcase_set_vm_power_state_task_timeout['ids']) +def test_set_vm_power_state_task_timeout(mocker, fake_ansible_module, XenAPI, xenserver, power_state, error_msg): + """Tests failure on async task timeout.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": "Running", + "Async.VM.clean_shutdown.return_value": fake_xenapi_ref('task'), + "Async.VM.clean_reboot.return_value": fake_xenapi_ref('task'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.wait_for_task', return_value="timeout") + + with pytest.raises(FailJsonException) as exc_info: + xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), power_state, timeout=1) + + # Beside VM.get_power_state() only one of Async.VM.clean_shutdown or + # Async.VM.clean_reboot should have been called additionally. + assert len(mocked_xenapi.method_calls) == 2 + + assert exc_info.value.kwargs['msg'] == error_msg + + +@pytest.mark.parametrize('power_state_desired, power_state_current', + testcase_set_vm_power_state_no_transitions['params'], + ids=testcase_set_vm_power_state_no_transitions['ids']) +def test_set_vm_power_state_no_transition(mocker, fake_ansible_module, XenAPI, xenserver, power_state_desired, power_state_current): + """Tests regular invocation without power state transition.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": power_state_current, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + result = xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), power_state_desired) + + # Beside VM.get_power_state() no other method should have been + # called additionally. + assert len(mocked_xenapi.method_calls) == 1 + + assert result[0] is False + assert result[1] == power_state_current.lower() + + +@pytest.mark.parametrize('power_state_desired, power_state_current, power_state_resulting, activated_xenapi_method', + testcase_set_vm_power_state_transitions['params'], + ids=testcase_set_vm_power_state_transitions['ids']) +def test_set_vm_power_state_transition(mocker, + fake_ansible_module, + XenAPI, + xenserver, + power_state_desired, + power_state_current, + power_state_resulting, + activated_xenapi_method): + """Tests regular invocation with power state transition.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": power_state_current, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + result = xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), power_state_desired, timeout=0) + + mocked_xenapi_method = mocked_xenapi + + for activated_xenapi_class in activated_xenapi_method.split('.'): + mocked_xenapi_method = getattr(mocked_xenapi_method, activated_xenapi_class) + + mocked_xenapi_method.assert_called_once() + + # Beside VM.get_power_state() only activated_xenapi_method should have + # been called additionally. + assert len(mocked_xenapi.method_calls) == 2 + + assert result[0] is True + assert result[1] == power_state_resulting + + +@pytest.mark.parametrize('power_state_desired, power_state_current, power_state_resulting, activated_xenapi_method', + testcase_set_vm_power_state_transitions_async['params'], + ids=testcase_set_vm_power_state_transitions_async['ids']) +def test_set_vm_power_state_transition_async(mocker, + fake_ansible_module, + XenAPI, + xenserver, + power_state_desired, + power_state_current, + power_state_resulting, + activated_xenapi_method): + """ + Tests regular invocation with async power state transition + (shutdownguest and rebootguest only). + """ + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": power_state_current, + "%s.return_value" % activated_xenapi_method: fake_xenapi_ref('task'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.wait_for_task', return_value="") + + result = xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), power_state_desired, timeout=1) + + mocked_xenapi_method = mocked_xenapi + + for activated_xenapi_class in activated_xenapi_method.split('.'): + mocked_xenapi_method = getattr(mocked_xenapi_method, activated_xenapi_class) + + mocked_xenapi_method.assert_called_once() + + # Beside VM.get_power_state() only activated_xenapi_method should have + # been called additionally. + assert len(mocked_xenapi.method_calls) == 2 + + assert result[0] is True + assert result[1] == power_state_resulting + + +@pytest.mark.parametrize('power_state_desired, power_state_current, power_state_resulting, activated_xenapi_method', + testcase_set_vm_power_state_transitions['params'], + ids=testcase_set_vm_power_state_transitions['ids']) +def test_set_vm_power_state_transition_check_mode(mocker, + fake_ansible_module, + XenAPI, + xenserver, + power_state_desired, + power_state_current, + power_state_resulting, + activated_xenapi_method): + """Tests regular invocation with power state transition in check mode.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": power_state_current, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + fake_ansible_module.check_mode = True + result = xenserver.set_vm_power_state(fake_ansible_module, fake_xenapi_ref('VM'), power_state_desired, timeout=0) + + mocked_xenapi_method = mocked_xenapi + + for activated_xenapi_class in activated_xenapi_method.split('.'): + mocked_xenapi_method = getattr(mocked_xenapi_method, activated_xenapi_class) + + mocked_xenapi_method.assert_not_called() + + # Beside VM.get_power_state() no other method should have been + # called additionally. + assert len(mocked_xenapi.method_calls) == 1 + + assert result[0] is True + assert result[1] == power_state_resulting diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_wait_for_functions.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_wait_for_functions.py new file mode 100644 index 000000000..3f31f030e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_wait_for_functions.py @@ -0,0 +1,221 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +from .FakeAnsibleModule import FailJsonException +from .common import fake_xenapi_ref, testcase_bad_xenapi_refs + + +testcase_wait_for_vm_ip_address_bad_power_states = { + "params": [ + 'Halted', + 'Paused', + 'Suspended', + 'Other', + ], + "ids": [ + 'state-halted', + 'state-paused', + 'state-suspended', + 'state-other', + ] +} + +testcase_wait_for_vm_ip_address_bad_guest_metrics = { + "params": [ + ('OpaqueRef:NULL', {"networks": {}}), + (fake_xenapi_ref('VM_guest_metrics'), {"networks": {}}), + ], + "ids": [ + 'vm_guest_metrics_ref-null, no-ip', + 'vm_guest_metrics_ref-ok, no-ip', + ], +} + +testcase_wait_for_task_all_statuses = { + "params": [ + ('Success', ''), + ('Failure', 'failure'), + ('Cancelling', 'cancelling'), + ('Cancelled', 'cancelled'), + ('Other', 'other'), + ], + "ids": [ + 'task-success', + 'task-failure', + 'task-cancelling', + 'task-cancelled', + 'task-other', + ] +} + + +@pytest.mark.parametrize('vm_ref', testcase_bad_xenapi_refs['params'], ids=testcase_bad_xenapi_refs['ids']) +def test_wait_for_vm_ip_address_bad_vm_ref(fake_ansible_module, xenserver, vm_ref): + """Tests failure on bad vm_ref.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.wait_for_vm_ip_address(fake_ansible_module, vm_ref) + + assert exc_info.value.kwargs['msg'] == "Cannot wait for VM IP address. Invalid VM reference supplied!" + + +def test_wait_for_vm_ip_address_xenapi_failure(mock_xenapi_failure, xenserver, fake_ansible_module): + """Tests catching of XenAPI failures.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.wait_for_vm_ip_address(fake_ansible_module, fake_xenapi_ref('VM')) + + assert exc_info.value.kwargs['msg'] == "XAPI ERROR: %s" % mock_xenapi_failure[1] + + +@pytest.mark.parametrize('bad_power_state', + testcase_wait_for_vm_ip_address_bad_power_states['params'], + ids=testcase_wait_for_vm_ip_address_bad_power_states['ids']) +def test_wait_for_vm_ip_address_bad_power_state(mocker, fake_ansible_module, XenAPI, xenserver, bad_power_state): + """Tests failure on bad power state.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": bad_power_state, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.wait_for_vm_ip_address(fake_ansible_module, fake_xenapi_ref('VM')) + + assert exc_info.value.kwargs['msg'] == ("Cannot wait for VM IP address when VM is in state '%s'!" % + xenserver.xapi_to_module_vm_power_state(bad_power_state.lower())) + + +@pytest.mark.parametrize('bad_guest_metrics_ref, bad_guest_metrics', + testcase_wait_for_vm_ip_address_bad_guest_metrics['params'], + ids=testcase_wait_for_vm_ip_address_bad_guest_metrics['ids']) +def test_wait_for_vm_ip_address_timeout(mocker, fake_ansible_module, XenAPI, xenserver, bad_guest_metrics_ref, bad_guest_metrics): + """Tests timeout.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "VM.get_power_state.return_value": "Running", + "VM.get_guest_metrics.return_value": bad_guest_metrics_ref, + "VM_guest_metrics.get_record.return_value": bad_guest_metrics, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('time.sleep') + + with pytest.raises(FailJsonException) as exc_info: + xenserver.wait_for_vm_ip_address(fake_ansible_module, fake_xenapi_ref('VM'), timeout=1) + + assert exc_info.value.kwargs['msg'] == "Timed out waiting for VM IP address!" + + +def test_wait_for_vm_ip_address(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests regular invocation.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + # This mock simulates regular VM IP acquirement lifecycle: + # + # 1) First, no guest metrics are available because VM is not yet fully + # booted and guest agent is not yet started. + # 2) Next, guest agent is started and guest metrics are available but + # IP address is still not acquired. + # 3) Lastly, IP address is acquired by VM on its primary VIF. + mocked_returns = { + "VM.get_power_state.return_value": "Running", + "VM.get_guest_metrics.side_effect": [ + 'OpaqueRef:NULL', + fake_xenapi_ref('VM_guest_metrics'), + fake_xenapi_ref('VM_guest_metrics'), + ], + "VM_guest_metrics.get_record.side_effect": [ + { + "networks": {}, + }, + { + "networks": { + "0/ip": "192.168.0.1", + "1/ip": "10.0.0.1", + }, + }, + ], + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('time.sleep') + + fake_guest_metrics = xenserver.wait_for_vm_ip_address(fake_ansible_module, fake_xenapi_ref('VM')) + + assert fake_guest_metrics == mocked_returns['VM_guest_metrics.get_record.side_effect'][1] + + +@pytest.mark.parametrize('task_ref', testcase_bad_xenapi_refs['params'], ids=testcase_bad_xenapi_refs['ids']) +def test_wait_for_task_bad_task_ref(fake_ansible_module, xenserver, task_ref): + """Tests failure on bad task_ref.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.wait_for_task(fake_ansible_module, task_ref) + + assert exc_info.value.kwargs['msg'] == "Cannot wait for task. Invalid task reference supplied!" + + +def test_wait_for_task_xenapi_failure(mock_xenapi_failure, fake_ansible_module, xenserver): + """Tests catching of XenAPI failures.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.wait_for_task(fake_ansible_module, fake_xenapi_ref('task')) + + assert exc_info.value.kwargs['msg'] == "XAPI ERROR: %s" % mock_xenapi_failure[1] + + +def test_wait_for_task_timeout(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests timeout.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "task.get_status.return_value": "Pending", + "task.destroy.return_value": None, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('time.sleep') + + fake_result = xenserver.wait_for_task(fake_ansible_module, fake_xenapi_ref('task'), timeout=1) + + mocked_xenapi.task.destroy.assert_called_once() + assert fake_result == "timeout" + + +@pytest.mark.parametrize('task_status, result', + testcase_wait_for_task_all_statuses['params'], + ids=testcase_wait_for_task_all_statuses['ids']) +def test_wait_for_task(mocker, fake_ansible_module, XenAPI, xenserver, task_status, result): + """Tests regular invocation.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + # Mock will first return Pending status and on second invocation it will + # return one of possible final statuses. + mocked_returns = { + "task.get_status.side_effect": [ + 'Pending', + task_status, + ], + "task.destroy.return_value": None, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('time.sleep') + + fake_result = xenserver.wait_for_task(fake_ansible_module, fake_xenapi_ref('task')) + + mocked_xenapi.task.destroy.assert_called_once() + assert fake_result == result diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xapi.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xapi.py new file mode 100644 index 000000000..86965b4e5 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xapi.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 atexit + +from .FakeAnsibleModule import FailJsonException +from ansible.module_utils.ansible_release import __version__ as ANSIBLE_VERSION + + +testcase_module_local_conn = { + "params": [ + { + "hostname": "localhost", + "username": "someuser", + "password": "somepwd", + "validate_certs": True, + }, + ], + "ids": [ + "local-conn", + ], +} + +testcase_module_remote_conn = { + "params": [ + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "validate_certs": True, + }, + ], + "ids": [ + "remote-conn", + ], +} + +testcase_module_remote_conn_scheme = { + "params": [ + { + "hostname": "http://somehost", + "username": "someuser", + "password": "somepwd", + "validate_certs": True, + }, + { + "hostname": "https://somehost", + "username": "someuser", + "password": "somepwd", + "validate_certs": True, + }, + ], + "ids": [ + "remote-conn-http", + "remote-conn-https", + ], +} + + +@pytest.mark.parametrize('fake_ansible_module', testcase_module_local_conn['params'], ids=testcase_module_local_conn['ids'], indirect=True) +def test_xapi_connect_local_session(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests that connection to localhost uses XenAPI.xapi_local() function.""" + mocker.patch('XenAPI.xapi_local') + + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + XenAPI.xapi_local.assert_called_once() + + +@pytest.mark.parametrize('fake_ansible_module', testcase_module_local_conn['params'], ids=testcase_module_local_conn['ids'], indirect=True) +def test_xapi_connect_local_login(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests that connection to localhost uses empty username and password.""" + mocker.patch.object(XenAPI.Session, 'login_with_password', create=True) + + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + XenAPI.Session.login_with_password.assert_called_once_with('', '', ANSIBLE_VERSION, 'Ansible') + + +def test_xapi_connect_login(mocker, fake_ansible_module, XenAPI, xenserver): + """ + Tests that username and password are properly propagated to + XenAPI.Session.login_with_password() function. + """ + mocker.patch.object(XenAPI.Session, 'login_with_password', create=True) + + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + username = fake_ansible_module.params['username'] + password = fake_ansible_module.params['password'] + + XenAPI.Session.login_with_password.assert_called_once_with(username, password, ANSIBLE_VERSION, 'Ansible') + + +def test_xapi_connect_login_failure(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests that login failure is properly handled.""" + fake_error_msg = "Fake XAPI login error!" + + mocked_login = mocker.patch.object(XenAPI.Session, 'login_with_password', create=True) + mocked_login.side_effect = XenAPI.Failure(fake_error_msg) + + hostname = fake_ansible_module.params['hostname'] + username = fake_ansible_module.params['username'] + + with pytest.raises(FailJsonException) as exc_info: + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + assert exc_info.value.kwargs['msg'] == "Unable to log on to XenServer at http://%s as %s: %s" % (hostname, username, fake_error_msg) + + +@pytest.mark.parametrize('fake_ansible_module', testcase_module_remote_conn_scheme['params'], ids=testcase_module_remote_conn_scheme['ids'], indirect=True) +def test_xapi_connect_remote_scheme(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests that explicit scheme in hostname param is preserved.""" + mocker.patch('XenAPI.Session') + + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + hostname = fake_ansible_module.params['hostname'] + ignore_ssl = not fake_ansible_module.params['validate_certs'] + + XenAPI.Session.assert_called_once_with(hostname, ignore_ssl=ignore_ssl) + + +@pytest.mark.parametrize('fake_ansible_module', testcase_module_remote_conn['params'], ids=testcase_module_remote_conn['ids'], indirect=True) +def test_xapi_connect_remote_no_scheme(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests that proper scheme is prepended to hostname without scheme.""" + mocker.patch('XenAPI.Session') + + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + hostname = fake_ansible_module.params['hostname'] + ignore_ssl = not fake_ansible_module.params['validate_certs'] + + XenAPI.Session.assert_called_once_with("http://%s" % hostname, ignore_ssl=ignore_ssl) + + +def test_xapi_connect_support_ignore_ssl(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests proper handling of ignore_ssl support.""" + mocked_session = mocker.patch('XenAPI.Session') + mocked_session.side_effect = TypeError() + + with pytest.raises(TypeError) as exc_info: + xapi_session = xenserver.XAPI.connect(fake_ansible_module) + + hostname = fake_ansible_module.params['hostname'] + ignore_ssl = not fake_ansible_module.params['validate_certs'] + + XenAPI.Session.assert_called_with("http://%s" % hostname) + + +def test_xapi_connect_no_disconnect_atexit(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests skipping registration of atexit disconnect handler.""" + mocker.patch('atexit.register') + + xapi_session = xenserver.XAPI.connect(fake_ansible_module, disconnect_atexit=False) + + atexit.register.assert_not_called() + + +def test_xapi_connect_singleton(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests if XAPI.connect() returns singleton.""" + mocker.patch('XenAPI.Session') + + xapi_session1 = xenserver.XAPI.connect(fake_ansible_module) + xapi_session2 = xenserver.XAPI.connect(fake_ansible_module) + + XenAPI.Session.assert_called_once() + assert xapi_session1 == xapi_session2 diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xenserverobject.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xenserverobject.py new file mode 100644 index 000000000..2d758fd4e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/xenserver/test_xenserverobject.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +from .FakeAnsibleModule import FailJsonException +from .common import fake_xenapi_ref + + +def test_xenserverobject_xenapi_lib_detection(mocker, fake_ansible_module, xenserver): + """Tests XenAPI lib detection code.""" + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.HAS_XENAPI', new=False) + + with pytest.raises(FailJsonException) as exc_info: + xenserver.XenServerObject(fake_ansible_module) + + assert 'Failed to import the required Python library (XenAPI) on' in exc_info.value.kwargs['msg'] + + +def test_xenserverobject_xenapi_failure(mock_xenapi_failure, fake_ansible_module, xenserver): + """Tests catching of XenAPI failures.""" + with pytest.raises(FailJsonException) as exc_info: + xenserver.XenServerObject(fake_ansible_module) + + assert exc_info.value.kwargs['msg'] == "XAPI ERROR: %s" % mock_xenapi_failure[1] + + +def test_xenserverobject(mocker, fake_ansible_module, XenAPI, xenserver): + """Tests successful creation of XenServerObject.""" + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "pool.get_all.return_value": [fake_xenapi_ref('pool')], + "pool.get_default_SR.return_value": fake_xenapi_ref('SR'), + "session.get_this_host.return_value": fake_xenapi_ref('host'), + "host.get_software_version.return_value": {"product_version": "7.2.0"}, + } + + mocked_xenapi.configure_mock(**mocked_returns) + + xso = xenserver.XenServerObject(fake_ansible_module) + + assert xso.pool_ref == fake_xenapi_ref('pool') + assert xso.xenserver_version == [7, 2, 0] diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/FakeAnsibleModule.py b/ansible_collections/community/general/tests/unit/plugins/modules/FakeAnsibleModule.py new file mode 100644 index 000000000..bdcc21793 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/FakeAnsibleModule.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + + +class AnsibleModuleException(Exception): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + +class ExitJsonException(AnsibleModuleException): + pass + + +class FailJsonException(AnsibleModuleException): + pass + + +class FakeAnsibleModule: + def __init__(self, params=None, check_mode=False): + self.params = params + self.check_mode = check_mode + + def exit_json(self, *args, **kwargs): + raise ExitJsonException(*args, **kwargs) + + def fail_json(self, *args, **kwargs): + raise FailJsonException(*args, **kwargs) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/FakeXenAPI.py b/ansible_collections/community/general/tests/unit/plugins/modules/FakeXenAPI.py new file mode 100644 index 000000000..bc9d69c77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/FakeXenAPI.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + +FAKE_API_VERSION = "1.1" + + +class Failure(Exception): + def __init__(self, details): + self.details = details + + def __str__(self): + return str(self.details) + + +class Session(object): + def __init__(self, uri, transport=None, encoding=None, verbose=0, + allow_none=1, ignore_ssl=False): + + self.transport = transport + self._session = None + self.last_login_method = None + self.last_login_params = None + self.API_version = FAKE_API_VERSION + + def _get_api_version(self): + return FAKE_API_VERSION + + def _login(self, method, params): + self._session = "OpaqueRef:fake-xenapi-session-ref" + self.last_login_method = method + self.last_login_params = params + self.API_version = self._get_api_version() + + def _logout(self): + self._session = None + self.last_login_method = None + self.last_login_params = None + self.API_version = FAKE_API_VERSION + + def xenapi_request(self, methodname, params): + if methodname.startswith('login'): + self._login(methodname, params) + return None + elif methodname == 'logout' or methodname == 'session.logout': + self._logout() + return None + else: + # Should be patched with mocker.patch(). + return None + + def __getattr__(self, name): + if name == 'handle': + return self._session + elif name == 'xenapi': + # Should be patched with mocker.patch(). + return None + elif name.startswith('login') or name.startswith('slave_local'): + return lambda *params: self._login(name, params) + elif name == 'logout': + return self._logout + + +def xapi_local(): + return Session("http://_var_lib_xcp_xapi/") diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/conftest.py b/ansible_collections/community/general/tests/unit/plugins/modules/conftest.py new file mode 100644 index 000000000..9504c2336 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/conftest.py @@ -0,0 +1,39 @@ +# Copyright (c) 2017 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 + +import pytest + +from ansible.module_utils.six import string_types +from ansible.module_utils.common.text.converters import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + +from ansible_collections.community.general.plugins.module_utils import deps + + +@pytest.fixture +def patch_ansible_module(request, mocker): + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the patch_ansible_module pytest fixture') + + mocker.patch('ansible.module_utils.basic._ANSIBLE_ARGS', to_bytes(args)) + + +@pytest.fixture(autouse=True) +def deps_cleanup(): + deps._deps.clear() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py b/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py new file mode 100644 index 000000000..c64d99fff --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py @@ -0,0 +1,704 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 httmock import response # noqa +from httmock import urlmatch # noqa + +from ansible_collections.community.general.tests.unit.compat import unittest + +import gitlab + + +class FakeAnsibleModule(object): + def __init__(self, module_params=None): + self.check_mode = False + self.params = module_params if module_params else {} + + def fail_json(self, **args): + pass + + def exit_json(self, **args): + pass + + +class GitlabModuleTestCase(unittest.TestCase): + def setUp(self): + unitest_python_version_check_requirement(self) + + self.mock_module = FakeAnsibleModule() + + self.gitlab_instance = gitlab.Gitlab("http://localhost", private_token="private_token", api_version=4) + + +# Python 2.7+ is needed for python-gitlab +GITLAB_MINIMUM_PYTHON_VERSION = (2, 7) + + +# Verify if the current Python version is higher than GITLAB_MINIMUM_PYTHON_VERSION +def python_version_match_requirement(): + return sys.version_info >= GITLAB_MINIMUM_PYTHON_VERSION + + +def python_gitlab_module_version(): + return gitlab.__version__ + + +def python_gitlab_version_match_requirement(): + return "2.3.0" + + +# Skip unittest test case if python version don't match requirement +def unitest_python_version_check_requirement(unittest_testcase): + if not python_version_match_requirement(): + unittest_testcase.skipTest("Python %s+ is needed for python-gitlab" % ",".join(map(str, GITLAB_MINIMUM_PYTHON_VERSION))) + + +''' +USER API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users", method="get") +def resp_find_user(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1, "username": "john_smith", "name": "John Smith", "state": "active",' + '"avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",' + '"web_url": "http://localhost:3000/john_smith"}, {"id": 2,' + '"username": "jack_smith", "name": "Jack Smith", "state": "blocked",' + '"avatar_url": "http://gravatar.com/../e32131cd8.jpeg",' + '"web_url": "http://localhost:3000/jack_smith"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users", method="post") +def resp_create_user(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "username": "john_smith", "name": "John Smith", "state": "active",' + '"avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",' + '"web_url": "http://localhost:3000/john_smith","created_at": "2012-05-23T08:00:58Z",' + '"bio": null, "location": null, "public_email": "john@example.com", "skype": "",' + '"linkedin": "", "twitter": "", "website_url": "", "organization": ""}') + content = content.encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="get") +def resp_get_user(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "username": "john_smith", "name": "John Smith",' + '"state": "active",' + '"avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",' + '"web_url": "http://localhost:3000/john_smith",' + '"created_at": "2012-05-23T08:00:58Z", "bio": null, "location": null,' + '"public_email": "john@example.com", "skype": "", "linkedin": "",' + '"twitter": "", "website_url": "", "organization": "", "is_admin": false}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="get") +def resp_get_missing_user(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(404, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="delete") +def resp_delete_user(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(204, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="delete") +def resp_delete_missing_user(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(404, content, headers, None, 5, request) + + +''' +USER SSHKEY API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1/keys", method="get") +def resp_get_user_keys(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1, "title": "Public key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596' + 'k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQa' + 'SeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",' + '"created_at": "2014-08-01T14:47:39.080Z"},{"id": 3,' + '"title": "Another Public key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596' + 'k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaS' + 'eP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",' + '"created_at": "2014-08-01T14:47:39.080Z"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1/keys", method="post") +def resp_create_user_keys(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "title": "Private key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDA1YotVDm2mAyk2tPt4E7AHm01sS6JZmcUdRuSuA5z' + 'szUJzYPPUSRAX3BCgTqLqYx//UuVncK7YqLVSbbwjKR2Ez5lISgCnVfLVEXzwhv+xawxKWmI7hJ5S0tOv6MJ+Ixy' + 'Ta4xcKwJTwB86z22n9fVOQeJTR2dSOH1WJrf0PvRk+KVNY2jTiGHTi9AIjLnyD/jWRpOgtdfkLRc8EzAWrWlgNmH' + '2WOKBw6za0az6XoG75obUdFVdW3qcD0xc809OHLi7FDf+E7U4wiZJCFuUizMeXyuK/SkaE1aee4Qp5R4dxTR4TP9' + 'M1XAYkf+kF0W9srZ+mhF069XD/zhUPJsvwEF",' + '"created_at": "2014-08-01T14:47:39.080Z"}') + content = content.encode("utf-8") + return response(201, content, headers, None, 5, request) + + +''' +GROUP API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups", method="get") +def resp_find_group(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1, "name": "Foobar Group", "path": "foo-bar",' + '"description": "An interesting group", "visibility": "public",' + '"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",' + '"web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false,' + '"full_name": "Foobar Group", "full_path": "foo-bar",' + '"file_template_project_id": 1, "parent_id": null, "projects": []}, {"id": 2, "name": "BarFoo Group", "path": "bar-foor",' + '"description": "An interesting group", "visibility": "public",' + '"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/2/bar.jpg",' + '"web_url": "http://localhost:3000/groups/bar-foo", "request_access_enabled": false,' + '"full_name": "BarFoo Group", "full_path": "bar-foo",' + '"file_template_project_id": 1, "parent_id": null, "projects": []}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1", method="get") +def resp_get_group(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "name": "Foobar Group", "path": "foo-bar",' + '"description": "An interesting group", "visibility": "public",' + '"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",' + '"web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false,' + '"full_name": "Foobar Group", "full_path": "foo-bar",' + '"project_creation_level": "maintainer", "subgroup_creation_level": "maintainer",' + '"require_two_factor_authentication": true,' + '"file_template_project_id": 1, "parent_id": null, "projects": [{"id": 1,"description": null, "default_branch": "master",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}]}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/foo-bar", method="get") +def resp_get_group_by_name(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "name": "Foobar Group", "path": "foo-bar",' + '"description": "An interesting group", "visibility": "public",' + '"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",' + '"web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false,' + '"full_name": "Foobar Group", "full_path": "foo-bar",' + '"project_creation_level": "maintainer", "subgroup_creation_level": "maintainer",' + '"require_two_factor_authentication": true,' + '"file_template_project_id": 1, "parent_id": null, "projects": [{"id": 1,"description": null, "default_branch": "master",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}]}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1", method="get") +def resp_get_missing_group(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(404, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups", method="post") +def resp_create_group(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "name": "Foobar Group", "path": "foo-bar",' + '"description": "An interesting group", "visibility": "public",' + '"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",' + '"web_url": "http://localhost:3000/groups/foo-bar", "request_access_enabled": false,' + '"full_name": "Foobar Group", "full_path": "foo-bar",' + '"file_template_project_id": 1, "parent_id": null,' + '"project_creation_level": "developer", "subgroup_creation_level": "maintainer",' + '"require_two_factor_authentication": true}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups", method="post") +def resp_create_subgroup(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 2, "name": "BarFoo Group", "path": "bar-foor",' + '"description": "An interesting group", "visibility": "public",' + '"lfs_enabled": true, "avatar_url": "http://localhost:3000/uploads/group/avatar/2/bar.jpg",' + '"web_url": "http://localhost:3000/groups/foo-bar/bar-foo", "request_access_enabled": false,' + '"full_name": "BarFoo Group", "full_path": "foo-bar/bar-foo",' + '"file_template_project_id": 1, "parent_id": 1,' + '"project_creation_level": "noone",' + '"require_two_factor_authentication": true}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/users/1", method="delete") +def resp_delete_group(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(204, content, headers, None, 5, request) + + +''' +GROUP MEMBER API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/members/1", method="get") +def resp_get_member(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "username": "raymond_smith", "name": "Raymond Smith", "state": "active",' + '"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",' + '"web_url": "http://192.168.1.8:3000/root", "expires_at": "2012-10-22T14:13:35Z", "access_level": 30}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/members", method="get") +def resp_find_member(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1, "username": "raymond_smith", "name": "Raymond Smith", "state": "active",' + '"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",' + '"web_url": "http://192.168.1.8:3000/root", "expires_at": "2012-10-22T14:13:35Z", "access_level": 30},{' + '"id": 2, "username": "john_doe", "name": "John Doe","state": "active",' + '"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",' + '"web_url": "http://192.168.1.8:3000/root","expires_at": "2012-10-22T14:13:35Z",' + '"access_level": 30}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/members", method="post") +def resp_add_member(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "username": "raymond_smith", "name": "Raymond Smith",' + '"state": "active",' + '"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",' + '"web_url": "http://192.168.1.8:3000/root", "expires_at": "2012-10-22T14:13:35Z",' + '"access_level": 30}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/members/1", method="put") +def resp_update_member(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "username": "raymond_smith", "name": "Raymond Smith",' + '"state": "active",' + '"avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon",' + '"web_url": "http://192.168.1.8:3000/root", "expires_at": "2012-10-22T14:13:35Z",' + '"access_level": 10}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +''' +DEPLOY KEY API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/deploy_keys", method="get") +def resp_find_project_deploy_key(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1,"title": "Public key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxc' + 'KDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",' + '"created_at": "2013-10-02T10:12:29Z"},{"id": 3,"title": "Another Public key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxc' + 'KDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",' + '"created_at": "2013-10-02T11:12:29Z"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/deploy_keys/1", method="get") +def resp_get_project_deploy_key(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"title": "Public key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxc' + 'KDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",' + '"created_at": "2013-10-02T10:12:29Z"}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/deploy_keys", method="post") +def resp_create_project_deploy_key(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"title": "Public key",' + '"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxc' + 'KDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",' + '"created_at": "2013-10-02T10:12:29Z"}') + content = content.encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/deploy_keys/1", method="delete") +def resp_delete_project_deploy_key(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(204, content, headers, None, 5, request) + + +''' +PROJECT API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="get") +def resp_find_project(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1,"description": null, "default_branch": "master", "merge_method": "merge",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1", method="get") +def resp_get_project(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"description": null, "default_branch": "master", "merge_method": "merge",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/foo-bar%2Fdiaspora-client", method="get") +def resp_get_project_by_name(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"description": null, "default_branch": "master", "merge_method": "merge",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/projects", method="get") +def resp_find_group_project(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1,"description": null, "default_branch": "master", "merge_method": "merge",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/projects/1", method="get") +def resp_get_group_project(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"description": null, "default_branch": "master", "merge_method": "merge",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects", method="post") +def resp_create_project(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"description": null, "default_branch": "master", "merge_method": "merge",' + '"ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git",' + '"http_url_to_repo": "http://example.com/diaspora/diaspora-client.git",' + '"web_url": "http://example.com/diaspora/diaspora-client",' + '"readme_url": "http://example.com/diaspora/diaspora-client/blob/master/README.md",' + '"tag_list": ["example","disapora client"],"name": "Diaspora Client",' + '"name_with_namespace": "Diaspora / Diaspora Client","path": "diaspora-client",' + '"path_with_namespace": "diaspora/diaspora-client","created_at": "2013-09-30T13:46:02Z",' + '"last_activity_at": "2013-09-30T13:46:02Z","forks_count": 0,' + '"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",' + '"star_count": 0}') + content = content.encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1", method="delete") +def resp_delete_project(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + + return response(204, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/protected_branches/master", method="get") +def resp_get_protected_branch(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1, "name": "master", "push_access_levels": [{"access_level": 40, "access_level_description": "Maintainers"}],' + '"merge_access_levels": [{"access_level": 40, "access_level_description": "Maintainers"}],' + '"allow_force_push":false, "code_owner_approval_required": false}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/protected_branches/master", method="get") +def resp_get_protected_branch_not_exist(url, request): + headers = {'content-type': 'application/json'} + content = ('') + content = content.encode("utf-8") + return response(404, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/protected_branches/master", method="delete") +def resp_delete_protected_branch(url, request): + headers = {'content-type': 'application/json'} + content = ('') + content = content.encode("utf-8") + return response(204, content, headers, None, 5, request) + + +''' +HOOK API +''' + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/hooks", method="get") +def resp_find_project_hook(url, request): + headers = {'content-type': 'application/json'} + content = ('[{"id": 1,"url": "http://example.com/hook","project_id": 3,' + '"push_events": true,"push_events_branch_filter": "","issues_events": true,' + '"confidential_issues_events": true,"merge_requests_events": true,' + '"tag_push_events": true,"note_events": true,"job_events": true,' + '"pipeline_events": true,"wiki_page_events": true,"enable_ssl_verification": true,' + '"created_at": "2012-10-12T17:04:47Z"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/hooks/1", method="get") +def resp_get_project_hook(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"url": "http://example.com/hook","project_id": 3,' + '"push_events": true,"push_events_branch_filter": "","issues_events": true,' + '"confidential_issues_events": true,"merge_requests_events": true,' + '"tag_push_events": true,"note_events": true,"job_events": true,' + '"pipeline_events": true,"wiki_page_events": true,"enable_ssl_verification": true,' + '"created_at": "2012-10-12T17:04:47Z"}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/hooks", method="post") +def resp_create_project_hook(url, request): + headers = {'content-type': 'application/json'} + content = ('{"id": 1,"url": "http://example.com/hook","project_id": 3,' + '"push_events": true,"push_events_branch_filter": "","issues_events": true,' + '"confidential_issues_events": true,"merge_requests_events": true,' + '"tag_push_events": true,"note_events": true,"job_events": true,' + '"pipeline_events": true,"wiki_page_events": true,"enable_ssl_verification": true,' + '"created_at": "2012-10-12T17:04:47Z"}') + content = content.encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/hooks/1", method="delete") +def resp_delete_project_hook(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(204, content, headers, None, 5, request) + + +''' +RUNNER API +''' + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/all$', method="get") +def resp_find_runners_all(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-1-20150125","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-2-20150125","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners$', method="get") +def resp_find_runners_list(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-1-20201214","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-2-20201214","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/projects/1/runners$', method="get") +def resp_find_project_runners(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-1-20220210","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-2-20220210","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/groups/1/runners$', method="get") +def resp_find_group_runners(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-3-20220210","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-4-20220210","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="put") +def resp_update_runner(url, request): + headers = {'content-type': 'application/json', + "X-Page": 1, + "X-Next-Page": 2, + "X-Per-Page": 1, + "X-Total-Pages": 1, + "X-Total": 2} + content = ('[{"active": true,"description": "test-1-20201214","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"},{"active": true,' + '"description": "test-2-20201214","id": 2,"ip_address": "127.0.0.1",' + '"is_shared": false,"name": null,"online": false,"status": "offline"}]') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="get") +def resp_get_runner(url, request): + headers = {'content-type': 'application/json'} + content = ('{"active": true,"description": "test-1-20150125","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"}') + content = content.encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners$', method="post") +def resp_create_runner(url, request): + headers = {'content-type': 'application/json'} + content = ('{"active": true,"description": "test-1-20150125","id": 1,' + '"is_shared": false,"ip_address": "127.0.0.1","name": null,' + '"online": true,"status": "online"}') + content = content.encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(scheme="http", netloc="localhost", path=r'/api/v4/runners/1$', method="delete") +def resp_delete_runner(url, request): + headers = {'content-type': 'application/json'} + content = ('{}') + content = content.encode("utf-8") + return response(204, content, headers, None, 5, request) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/hpe_test_utils.py b/ansible_collections/community/general/tests/unit/plugins/modules/hpe_test_utils.py new file mode 100644 index 000000000..ab16d8f22 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/hpe_test_utils.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +# +# Copyright (2016-2017) Hewlett Packard Enterprise Development LP +# 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 re +import yaml + +from mock import Mock, patch +from .oneview_module_loader import ONEVIEW_MODULE_UTILS_PATH +from hpOneView.oneview_client import OneViewClient + + +class OneViewBaseTest(object): + @pytest.fixture(autouse=True) + def setUp(self, mock_ansible_module, mock_ov_client, request): + marker = request.node.get_marker('resource') + self.resource = getattr(mock_ov_client, "%s" % (marker.args)) + self.mock_ov_client = mock_ov_client + self.mock_ansible_module = mock_ansible_module + + @pytest.fixture + def testing_module(self): + resource_name = type(self).__name__.replace('Test', '') + resource_module_path_name = resource_name.replace('Module', '') + resource_module_path_name = re.findall('[A-Z][^A-Z]*', resource_module_path_name) + resource_module_path_name = 'oneview_' + str.join('_', resource_module_path_name).lower() + + ansible_collections = __import__('ansible_collections') + oneview_module = ansible_collections.community.general.plugins.modules + resource_module = getattr(oneview_module, resource_module_path_name) + self.testing_class = getattr(resource_module, resource_name) + testing_module = self.testing_class.__module__.split('.')[-1] + testing_module = getattr(oneview_module, testing_module) + try: + # Load scenarios from module examples (Also checks if it is a valid yaml) + EXAMPLES = yaml.safe_load(testing_module.EXAMPLES) + + except yaml.scanner.ScannerError: + message = "Something went wrong while parsing yaml from {0}.EXAMPLES".format(self.testing_class.__module__) + raise Exception(message) + return testing_module + + def test_main_function_should_call_run_method(self, testing_module, mock_ansible_module): + mock_ansible_module.params = {'config': 'config.json'} + + main_func = getattr(testing_module, 'main') + + with patch.object(self.testing_class, "run") as mock_run: + main_func() + mock_run.assert_called_once() + + +class FactsParamsTest(OneViewBaseTest): + def test_should_get_all_using_filters(self, testing_module): + self.resource.get_all.return_value = [] + + params_get_all_with_filters = dict( + config='config.json', + name=None, + params={ + 'start': 1, + 'count': 3, + 'sort': 'name:descending', + 'filter': 'purpose=General', + 'query': 'imported eq true' + }) + self.mock_ansible_module.params = params_get_all_with_filters + + self.testing_class().run() + + self.resource.get_all.assert_called_once_with(start=1, count=3, sort='name:descending', filter='purpose=General', query='imported eq true') + + def test_should_get_all_without_params(self, testing_module): + self.resource.get_all.return_value = [] + + params_get_all_with_filters = dict( + config='config.json', + name=None + ) + self.mock_ansible_module.params = params_get_all_with_filters + + self.testing_class().run() + + self.resource.get_all.assert_called_once_with() + + +class OneViewBaseTestCase(object): + mock_ov_client_from_json_file = None + testing_class = None + mock_ansible_module = None + mock_ov_client = None + testing_module = None + EXAMPLES = None + + def configure_mocks(self, test_case, testing_class): + """ + Preload mocked OneViewClient instance and AnsibleModule + Args: + test_case (object): class instance (self) that are inheriting from OneViewBaseTestCase + testing_class (object): class being tested + """ + self.testing_class = testing_class + + # Define OneView Client Mock (FILE) + patcher_json_file = patch.object(OneViewClient, 'from_json_file') + test_case.addCleanup(patcher_json_file.stop) + self.mock_ov_client_from_json_file = patcher_json_file.start() + + # Define OneView Client Mock + self.mock_ov_client = self.mock_ov_client_from_json_file.return_value + + # Define Ansible Module Mock + patcher_ansible = patch(ONEVIEW_MODULE_UTILS_PATH + '.AnsibleModule') + test_case.addCleanup(patcher_ansible.stop) + mock_ansible_module = patcher_ansible.start() + self.mock_ansible_module = Mock() + mock_ansible_module.return_value = self.mock_ansible_module + + self.__set_module_examples() + + def test_main_function_should_call_run_method(self): + self.mock_ansible_module.params = {'config': 'config.json'} + + main_func = getattr(self.testing_module, 'main') + + with patch.object(self.testing_class, "run") as mock_run: + main_func() + mock_run.assert_called_once() + + def __set_module_examples(self): + # Load scenarios from module examples (Also checks if it is a valid yaml) + ansible_collections = __import__('ansible_collections') + testing_module = self.testing_class.__module__.split('.')[-1] + self.testing_module = getattr(ansible_collections.community.general.plugins.modules, testing_module) + + try: + # Load scenarios from module examples (Also checks if it is a valid yaml) + self.EXAMPLES = yaml.safe_load(self.testing_module.EXAMPLES) + + except yaml.scanner.ScannerError: + message = "Something went wrong while parsing yaml from {0}.EXAMPLES".format(self.testing_class.__module__) + raise Exception(message) + + +class FactsParamsTestCase(OneViewBaseTestCase): + """ + FactsParamsTestCase has common test for classes that support pass additional + parameters when retrieving all resources. + """ + + def configure_client_mock(self, resorce_client): + """ + Args: + resorce_client: Resource client that is being called + """ + self.resource_client = resorce_client + + def __validations(self): + if not self.testing_class: + raise Exception("Mocks are not configured, you must call 'configure_mocks' before running this test.") + + if not self.resource_client: + raise Exception( + "Mock for the client not configured, you must call 'configure_client_mock' before running this test.") + + def test_should_get_all_using_filters(self): + self.__validations() + self.resource_client.get_all.return_value = [] + + params_get_all_with_filters = dict( + config='config.json', + name=None, + params={ + 'start': 1, + 'count': 3, + 'sort': 'name:descending', + 'filter': 'purpose=General', + 'query': 'imported eq true' + }) + self.mock_ansible_module.params = params_get_all_with_filters + + self.testing_class().run() + + self.resource_client.get_all.assert_called_once_with(start=1, count=3, sort='name:descending', + filter='purpose=General', + query='imported eq true') + + def test_should_get_all_without_params(self): + self.__validations() + self.resource_client.get_all.return_value = [] + + params_get_all_with_filters = dict( + config='config.json', + name=None + ) + self.mock_ansible_module.params = params_get_all_with_filters + + self.testing_class().run() + + self.resource_client.get_all.assert_called_once_with() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file-README.md b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file-README.md new file mode 100644 index 000000000..aa9298f37 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file-README.md @@ -0,0 +1,27 @@ + + +# interfaces_file unit tests + +## Tests structure + +- `input` directory contains interfaces configuration files +- `test_interfaces_file.py` runs each hardcoded test against all configurations in `input` directory and compares results with golden outputs in `golden_output` + +## Running unit tests with docker + +1. Clone project to `ansible_collections/community/general` +2. Change directory to the project one `cd ansible_collections/community/general` +3. Run `ansible-test units --docker -v --python 3.10 tests/unit/plugins/modules/system/interfaces_file/test_interfaces_file.py` + +## Adding tests + +1. New configurations should added to `input` directory +2. New test cases should be defined in `test_interfaces_file.py`. Same for new test functions if needed +3. On first test run for a new combination of a test case and an interface configuration new set of golden files will be generated. In case of docker-based test approach that's going to fail due to RO mount option. The workaround is to run tests locally with Python 3 (3.7 in this example): + 1. Install required modules with `pip3.7 install pytest-xdist pytest-mock mock` + 3. Run tests with `ansible-test units --python 3.10 tests/unit/plugins/modules/system/interfaces_file/test_interfaces_file.py` +4. Carefully verify newly created golden output files! diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt new file mode 100644 index 000000000..bb6a333ab --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt new file mode 100644 index 000000000..f1bdb5fd1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt new file mode 100644 index 000000000..53c9acd13 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "absent", + "value": null +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt new file mode 100644 index 000000000..122f18652 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "absent", + "value": null +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4 new file mode 100644 index 000000000..9a2f5b059 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4 @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.42 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up new file mode 100644 index 000000000..5077e3a68 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up @@ -0,0 +1,13 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + post-up XXXX_ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up new file mode 100644 index 000000000..5c0f69736 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up @@ -0,0 +1,13 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + pre-up XXXX_ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6 new file mode 100644 index 000000000..afaaac962 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6 @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::42 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up new file mode 100644 index 000000000..cb3e98b77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up @@ -0,0 +1,13 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 + post-up XXXX_ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up new file mode 100644 index 000000000..149da568b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up @@ -0,0 +1,13 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 + pre-up XXXX_ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt new file mode 100644 index 000000000..6e0ba79f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt @@ -0,0 +1,8 @@ +fail_json message: Error: interface eth1 not found +options: +{ + "iface": "eth1", + "option": "method", + "state": "present", + "value": "dhcp" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu new file mode 100644 index 000000000..40331271a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu @@ -0,0 +1,13 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 + mtu 1350 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt new file mode 100644 index 000000000..8b9c5a14b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "mtu", + "state": "present", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt new file mode 100644 index 000000000..a6ce9ad69 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "slaves", + "state": "present", + "value": "int1 int3" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json new file mode 100644 index 000000000..8903ee647 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "fc00::1", + "address_family": "inet6", + "down": [], + "method": "static", + "post-up": [ + "echo configuring ipv6" + ], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/address_family_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt new file mode 100644 index 000000000..bb6a333ab --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt new file mode 100644 index 000000000..f1bdb5fd1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt new file mode 100644 index 000000000..53c9acd13 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "absent", + "value": null +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt new file mode 100644 index 000000000..122f18652 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "absent", + "value": null +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4 new file mode 100644 index 000000000..696293741 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4 @@ -0,0 +1,7 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp + address 192.168.0.42 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up new file mode 100644 index 000000000..998f48446 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up @@ -0,0 +1,7 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp + post-up XXXX_ipv4 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up new file mode 100644 index 000000000..5e6af40a2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up @@ -0,0 +1,7 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp + pre-up XXXX_ipv4 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6 new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6 @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt new file mode 100644 index 000000000..015052275 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "address", + "state": "present", + "value": "fc00::42" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt new file mode 100644 index 000000000..2a73a2b77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "post-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt new file mode 100644 index 000000000..262ffe9f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "pre-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt new file mode 100644 index 000000000..6e0ba79f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt @@ -0,0 +1,8 @@ +fail_json message: Error: interface eth1 not found +options: +{ + "iface": "eth1", + "option": "method", + "state": "present", + "value": "dhcp" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu new file mode 100644 index 000000000..7bbad22a5 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu @@ -0,0 +1,7 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp + mtu 1350 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt new file mode 100644 index 000000000..8b9c5a14b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "mtu", + "state": "present", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt new file mode 100644 index 000000000..a6ce9ad69 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "slaves", + "state": "present", + "value": "int1 int3" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json new file mode 100644 index 000000000..782b4d0fb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json @@ -0,0 +1,18 @@ +{ + "eth0": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/default_dhcp_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt new file mode 100644 index 000000000..bb6a333ab --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt new file mode 100644 index 000000000..f1bdb5fd1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt new file mode 100644 index 000000000..53c9acd13 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "absent", + "value": null +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt new file mode 100644 index 000000000..122f18652 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt @@ -0,0 +1,17 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "absent", + "value": null +} +===== +[1] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "up", + "state": "present", + "value": "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4 new file mode 100644 index 000000000..ff999d718 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4 @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 192.168.0.42 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up new file mode 100644 index 000000000..36c773be6 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up @@ -0,0 +1,9 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 +post-up XXXX_ipv4 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up new file mode 100644 index 000000000..89264c1d1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up @@ -0,0 +1,9 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 +pre-up XXXX_ipv4 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6 new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6 @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt new file mode 100644 index 000000000..015052275 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "address", + "state": "present", + "value": "fc00::42" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt new file mode 100644 index 000000000..2a73a2b77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "post-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt new file mode 100644 index 000000000..262ffe9f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "pre-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt new file mode 100644 index 000000000..6e0ba79f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt @@ -0,0 +1,8 @@ +fail_json message: Error: interface eth1 not found +options: +{ + "iface": "eth1", + "option": "method", + "state": "present", + "value": "dhcp" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert new file mode 100644 index 000000000..9da7b7259 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert @@ -0,0 +1,7 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu new file mode 100644 index 000000000..4788c3ddf --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1350 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt new file mode 100644 index 000000000..8b9c5a14b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "mtu", + "state": "present", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt new file mode 100644 index 000000000..a6ce9ad69 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt @@ -0,0 +1,8 @@ +[0] fail_json message: Error: interface aggi not found +options: +{ + "iface": "aggi", + "option": "slaves", + "state": "present", + "value": "int1 int3" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json new file mode 100644 index 000000000..5fe55437a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json @@ -0,0 +1,21 @@ +{ + "eth0": { + "address": "10.0.0.1", + "address_family": "inet", + "down": [], + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.0", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/no_leading_spaces_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up new file mode 100644 index 000000000..e86b25782 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up @@ -0,0 +1,62 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice new file mode 100644 index 000000000..e86b25782 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice @@ -0,0 +1,62 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup new file mode 100644 index 000000000..e86b25782 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup @@ -0,0 +1,62 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4 new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4 @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt new file mode 100644 index 000000000..bab7cce06 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet", + "iface": "eth0", + "option": "address", + "state": "present", + "value": "192.168.0.42" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt new file mode 100644 index 000000000..d1ce15975 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet", + "iface": "eth0", + "option": "post-up", + "state": "present", + "value": "XXXX_ipv4" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt new file mode 100644 index 000000000..8a439db4c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet", + "iface": "eth0", + "option": "pre-up", + "state": "present", + "value": "XXXX_ipv4" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6 new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6 @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt new file mode 100644 index 000000000..015052275 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "address", + "state": "present", + "value": "fc00::42" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt new file mode 100644 index 000000000..2a73a2b77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "post-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt new file mode 100644 index 000000000..262ffe9f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "pre-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method new file mode 100644 index 000000000..065bf0f04 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet dhcp + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json new file mode 100644 index 000000000..6df01a42f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "dhcp", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt new file mode 100644 index 000000000..57f3fe6f7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt @@ -0,0 +1,8 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "iface": "eth0", + "option": "mtu", + "state": "absent", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu new file mode 100644 index 000000000..5218eed19 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1350 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt new file mode 100644 index 000000000..007dd4444 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt @@ -0,0 +1,8 @@ +[1] fail_json message: Error: interface eth0 not found +options: +{ + "iface": "eth0", + "option": "mtu", + "state": "present", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves new file mode 100644 index 000000000..e2b78e93a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int3 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json new file mode 100644 index 000000000..c85421197 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json @@ -0,0 +1,109 @@ +{ + "agge": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "down": [], + "hwaddress": "ether 22:44:77:88:D5:96", + "method": "static", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K aggi tx off tso off" + ], + "pre-up": [], + "slaves": "int1 int2", + "up": [] + }, + "br0": { + "address": "188.44.133.76", + "address_family": "inet", + "bond_downdelay": "200", + "bond_lacp_rate": "slow", + "bond_miimon": "100", + "bond_mode": "4", + "bond_updelay": "200", + "bond_xmit_hash_policy": "layer3+4", + "bridge_ports": "agge", + "down": [], + "gateway": "188.44.133.75", + "hwaddress": "ether 22:44:77:88:D5:98", + "method": "static", + "netmask": "255.255.255.248", + "post-up": [ + "/sbin/ethtool -K agge tx off tso off" + ], + "pre-up": [], + "slaves": "ext1 ext2", + "up": [ + "route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi", + "route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi", + "route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi" + ] + }, + "eth1": { + "address_family": "inet", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext1": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "ext2": { + "address_family": "inet", + "bond-master": "agge", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int1": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "int2": { + "address_family": "inet", + "bond-master": "aggi", + "down": [], + "method": "manual", + "post-up": [], + "pre-up": [], + "up": [] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/servers.com_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup.test_no_changes.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_aggi_up_twice.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up new file mode 100644 index 000000000..2b5ca7404 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up @@ -0,0 +1,9 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_add_and_delete_aggi_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup new file mode 100644 index 000000000..326291ef2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup @@ -0,0 +1,10 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_aggi_remove_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4 new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4 @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt new file mode 100644 index 000000000..bab7cce06 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet", + "iface": "eth0", + "option": "address", + "state": "present", + "value": "192.168.0.42" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt new file mode 100644 index 000000000..d1ce15975 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet", + "iface": "eth0", + "option": "post-up", + "state": "present", + "value": "XXXX_ipv4" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt new file mode 100644 index 000000000..8a439db4c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet", + "iface": "eth0", + "option": "pre-up", + "state": "present", + "value": "XXXX_ipv4" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv4_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6 b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6 new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6 @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt new file mode 100644 index 000000000..015052275 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "address", + "state": "present", + "value": "fc00::42" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt new file mode 100644 index 000000000..2a73a2b77 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "post-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_post_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt new file mode 100644 index 000000000..262ffe9f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt @@ -0,0 +1,9 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "address_family": "inet6", + "iface": "eth0", + "option": "pre-up", + "state": "present", + "value": "XXXX_ipv6" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_ipv6_pre_up.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt new file mode 100644 index 000000000..6e0ba79f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt @@ -0,0 +1,8 @@ +fail_json message: Error: interface eth1 not found +options: +{ + "iface": "eth1", + "option": "method", + "state": "present", + "value": "dhcp" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_change_method.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt new file mode 100644 index 000000000..57f3fe6f7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt @@ -0,0 +1,8 @@ +fail_json message: Error: interface eth0 not found +options: +{ + "iface": "eth0", + "option": "mtu", + "state": "absent", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_revert.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu new file mode 100644 index 000000000..6bc202e0c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1350 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt new file mode 100644 index 000000000..007dd4444 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt @@ -0,0 +1,8 @@ +[1] fail_json message: Error: interface eth0 not found +options: +{ + "iface": "eth0", + "option": "mtu", + "state": "present", + "value": "1350" +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.exceptions.txt.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_and_eth0_mtu.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves new file mode 100644 index 000000000..d044b9251 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves @@ -0,0 +1,12 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + slaves int1 int3 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.exceptions.txt b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.exceptions.txt new file mode 100644 index 000000000..e69de29bb diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json new file mode 100644 index 000000000..80b7c210c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json @@ -0,0 +1,24 @@ +{ + "aggi": { + "address": "10.44.15.196", + "address_family": "inet", + "down": [], + "method": "dhcp", + "mtu": "1500", + "netmask": "255.255.255.248", + "post-up": [], + "pre-up": [], + "up": [ + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi", + "route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi" + ] + }, + "lo": { + "address_family": "inet", + "down": [], + "method": "loopback", + "post-up": [], + "pre-up": [], + "up": [] + } +} diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.json.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/golden_output/up_down_dup_set_aggi_slaves.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family new file mode 100644 index 000000000..bc4ecea78 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family @@ -0,0 +1,12 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet static + address 192.168.0.1 + post-up echo configuring ipv4 + +iface eth0 inet6 static + address fc00::1 + post-up echo configuring ipv6 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/address_family.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp new file mode 100644 index 000000000..bd4522ec0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp @@ -0,0 +1,6 @@ +# The loopback network interface +auto lo eth0 +iface lo inet loopback + +# The primary network interface +iface eth0 inet dhcp diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/default_dhcp.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces new file mode 100644 index 000000000..11f2d550c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces @@ -0,0 +1,8 @@ +iface lo inet loopback +auto lo + +auto eth0 +iface eth0 inet static +address 10.0.0.1 +netmask 255.255.255.0 +mtu 1500 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/no_leading_spaces.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com new file mode 100644 index 000000000..c826bbe73 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com @@ -0,0 +1,61 @@ + auto aggi + iface aggi inet static + hwaddress ether 22:44:77:88:D5:96 + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + slaves int1 int2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K aggi tx off tso off + + auto agge + iface agge inet manual + + auto br0 + iface br0 inet static + bridge_ports agge + hwaddress ether 22:44:77:88:D5:98 + address 188.44.133.76 + netmask 255.255.255.248 + gateway 188.44.133.75 + slaves ext1 ext2 + bond_mode 4 + bond_miimon 100 + bond_downdelay 200 + bond_updelay 200 + bond_lacp_rate slow + bond_xmit_hash_policy layer3+4 + post-up /sbin/ethtool -K agge tx off tso off + + up route add -net 10.0.0.0/8 gw 10.44.15.117 dev aggi + up route add -net 192.168.0.0/16 gw 10.44.15.117 dev aggi + up route add -net 188.44.208.0/21 gw 10.44.15.117 dev aggi + + auto int1 + iface int1 inet manual + bond-master aggi + + auto int2 + iface int2 inet manual + bond-master aggi + + auto ext1 + iface ext1 inet manual + bond-master agge + + auto ext2 + iface ext2 inet manual + bond-master agge + + auto eth1 + iface eth1 inet manual + + auto lo + iface lo inet loopback + +source /etc/network/interfaces.d/*.cfg diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/servers.com.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup new file mode 100644 index 000000000..fdf434eb4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup @@ -0,0 +1,11 @@ +# this file covers duplicates issue for up/down option, #3841 +auto lo +iface lo inet loopback + +auto aggi +iface aggi inet dhcp + address 10.44.15.196 + netmask 255.255.255.248 + mtu 1500 + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi + up route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup.license b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup.license new file mode 100644 index 000000000..edff8c768 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/interfaces_file_fixtures/input/up_down_dup.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: Ansible Project diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/test_interfaces_file.py b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/test_interfaces_file.py new file mode 100644 index 000000000..94e10b75f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/interfaces_file/test_interfaces_file.py @@ -0,0 +1,557 @@ +# Copyright (c) 2017, Roman Belyakovsky +# +# This file is part of Ansible +# +# 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.plugins.modules import interfaces_file +from shutil import copyfile, move +import difflib +import inspect +import io +import json +import os +import re +import shutil +import tempfile + + +class AnsibleFailJson(Exception): + pass + + +class ModuleMocked: + def atomic_move(self, src, dst): + move(src, dst) + + def backup_local(self, path): + backupp = os.path.join("/tmp", os.path.basename(path) + ".bak") + copyfile(path, backupp) + return backupp + + def fail_json(self, msg): + raise AnsibleFailJson(msg) + + +module = ModuleMocked() +fixture_path = os.path.join(os.path.dirname(__file__), 'interfaces_file_fixtures', 'input') +golden_output_path = os.path.join(os.path.dirname(__file__), 'interfaces_file_fixtures', 'golden_output') + + +class TestInterfacesFileModule(unittest.TestCase): + unittest.TestCase.maxDiff = None + + def getTestFiles(self, include_filter=None, exclude_filter=None): + flist = next(os.walk(fixture_path))[2] + flist = [file for file in flist if not file.endswith('.license')] + if include_filter: + flist = filter(lambda x: re.match(include_filter, x), flist) + if exclude_filter: + flist = filter(lambda x: not re.match(exclude_filter, x), flist) + return flist + + def compareFileToBackup(self, path, backup): + with open(path) as f1: + with open(backup) as f2: + diffs = difflib.context_diff(f1.readlines(), + f2.readlines(), + fromfile=os.path.basename(path), + tofile=os.path.basename(backup)) + # Restore backup + move(backup, path) + deltas = list(diffs) + self.assertTrue(len(deltas) == 0) + + def compareInterfacesLinesToFile(self, interfaces_lines, path, testname=None): + if not testname: + testname = "%s.%s" % (path, inspect.stack()[1][3]) + self.compareStringWithFile("".join([d['line'] for d in interfaces_lines if 'line' in d]), testname) + + def compareInterfacesToFile(self, ifaces, path, testname=None): + if not testname: + testname = "%s.%s.json" % (path, inspect.stack()[1][3]) + + testfilepath = os.path.join(golden_output_path, testname) + string = json.dumps(ifaces, sort_keys=True, indent=4, separators=(',', ': ')) + if string and not string.endswith('\n'): + string += '\n' + goldenstring = string + goldenData = ifaces + if not os.path.isfile(testfilepath): + with io.open(testfilepath, 'wb') as f: + f.write(string.encode()) + else: + with open(testfilepath, 'r') as goldenfile: + goldenData = json.load(goldenfile) + self.assertEqual(goldenData, ifaces) + + def compareStringWithFile(self, string, path): + testfilepath = os.path.join(golden_output_path, path) + if string and not string.endswith('\n'): + string += '\n' + goldenstring = string + if not os.path.isfile(testfilepath): + f = io.open(testfilepath, 'wb') + f.write(string.encode()) + f.close() + else: + with open(testfilepath, 'r') as goldenfile: + goldenstring = goldenfile.read() + goldenfile.close() + self.assertEqual(goldenstring, string) + + def test_no_changes(self): + for testfile in self.getTestFiles(): + path = os.path.join(fixture_path, testfile) + lines, ifaces = interfaces_file.read_interfaces_file(module, path) + self.compareInterfacesLinesToFile(lines, testfile) + self.compareInterfacesToFile(ifaces, testfile) + + def test_add_up_option_to_aggi(self): + testcases = { + "add_aggi_up": [ + { + 'iface': 'aggi', + 'option': 'up', + 'value': 'route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi', + 'state': 'present', + } + ], + "add_and_delete_aggi_up": [ + { + 'iface': 'aggi', + 'option': 'up', + 'value': 'route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi', + 'state': 'present', + }, + { + 'iface': 'aggi', + 'option': 'up', + 'value': None, + 'state': 'absent', + }, + ], + "add_aggi_up_twice": [ + { + 'iface': 'aggi', + 'option': 'up', + 'value': 'route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi', + 'state': 'present', + }, + { + 'iface': 'aggi', + 'option': 'up', + 'value': 'route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi', + 'state': 'present', + }, + ], + "aggi_remove_dup": [ + { + 'iface': 'aggi', + 'option': 'up', + 'value': None, + 'state': 'absent', + }, + { + 'iface': 'aggi', + 'option': 'up', + 'value': 'route add -net 224.0.0.0 netmask 240.0.0.0 dev aggi', + 'state': 'present', + }, + ], + "set_aggi_slaves": [ + { + 'iface': 'aggi', + 'option': 'slaves', + 'value': 'int1 int3', + 'state': 'present', + }, + ], + "set_aggi_and_eth0_mtu": [ + { + 'iface': 'aggi', + 'option': 'mtu', + 'value': '1350', + 'state': 'present', + }, + { + 'iface': 'eth0', + 'option': 'mtu', + 'value': '1350', + 'state': 'present', + }, + ], + } + for testname, options_list in testcases.items(): + for testfile in self.getTestFiles(): + path = os.path.join(fixture_path, testfile) + lines, ifaces = interfaces_file.read_interfaces_file(module, path) + fail_json_iterations = [] + for i, options in enumerate(options_list): + try: + dummy, lines = interfaces_file.set_interface_option(module, lines, options['iface'], options['option'], + options['value'], options['state']) + except AnsibleFailJson as e: + fail_json_iterations.append("[%d] fail_json message: %s\noptions:\n%s" % + (i, str(e), json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')))) + self.compareStringWithFile("\n=====\n".join(fail_json_iterations), "%s_%s.exceptions.txt" % (testfile, testname)) + + self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname)) + self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname)) + + def test_revert(self): + testcases = { + "revert": [ + { + 'iface': 'eth0', + 'option': 'mtu', + 'value': '1350', + } + ], + } + for testname, options_list in testcases.items(): + for testfile in self.getTestFiles(): + with tempfile.NamedTemporaryFile() as temp_file: + src_path = os.path.join(fixture_path, testfile) + path = temp_file.name + shutil.copy(src_path, path) + lines, ifaces = interfaces_file.read_interfaces_file(module, path) + backupp = module.backup_local(path) + options = options_list[0] + for state in ['present', 'absent']: + fail_json_iterations = [] + options['state'] = state + try: + dummy, lines = interfaces_file.set_interface_option(module, lines, + options['iface'], options['option'], options['value'], options['state']) + except AnsibleFailJson as e: + fail_json_iterations.append("fail_json message: %s\noptions:\n%s" % + (str(e), json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')))) + interfaces_file.write_changes(module, [d['line'] for d in lines if 'line' in d], path) + + self.compareStringWithFile("\n=====\n".join(fail_json_iterations), "%s_%s.exceptions.txt" % (testfile, testname)) + + self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname)) + self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname)) + if testfile not in ["no_leading_spaces"]: + # skip if eth0 has MTU value + self.compareFileToBackup(path, backupp) + + def test_change_method(self): + testcases = { + "change_method": [ + { + 'iface': 'eth1', + 'option': 'method', + 'value': 'dhcp', + 'state': 'present', + } + ], + } + for testname, options_list in testcases.items(): + for testfile in self.getTestFiles(): + with tempfile.NamedTemporaryFile() as temp_file: + src_path = os.path.join(fixture_path, testfile) + path = temp_file.name + shutil.copy(src_path, path) + lines, ifaces = interfaces_file.read_interfaces_file(module, path) + backupp = module.backup_local(path) + options = options_list[0] + fail_json_iterations = [] + try: + changed, lines = interfaces_file.set_interface_option(module, lines, options['iface'], options['option'], + options['value'], options['state']) + # When a changed is made try running it again for proper idempotency + if changed: + changed_again, lines = interfaces_file.set_interface_option(module, lines, options['iface'], + options['option'], options['value'], options['state']) + self.assertFalse(changed_again, + msg='Second request for change should return false for {0} running on {1}'.format(testname, + testfile)) + except AnsibleFailJson as e: + fail_json_iterations.append("fail_json message: %s\noptions:\n%s" % + (str(e), json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')))) + interfaces_file.write_changes(module, [d['line'] for d in lines if 'line' in d], path) + + self.compareStringWithFile("\n=====\n".join(fail_json_iterations), "%s_%s.exceptions.txt" % (testfile, testname)) + + self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname)) + self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname)) + # Restore backup + move(backupp, path) + + def test_getValueFromLine(self): + testcases = [ + { + "line": " address 1.2.3.5", + "value": "1.2.3.5", + } + ] + for testcase in testcases: + value = interfaces_file.getValueFromLine(testcase["line"]) + self.assertEqual(testcase["value"], value) + + def test_get_interface_options(self): + testcases = { + "basic": { + "iface_lines": [ + { + "address_family": "inet", + "iface": "eno1", + "line": "iface eno1 inet static", + "line_type": "iface", + "params": { + "address": "", + "address_family": "inet", + "down": [], + "gateway": "", + "method": "static", + "netmask": "", + "post-up": [], + "pre-up": [], + "up": [] + } + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " address 1.2.3.5", + "line_type": "option", + "option": "address", + "value": "1.2.3.5" + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " netmask 255.255.255.0", + "line_type": "option", + "option": "netmask", + "value": "255.255.255.0" + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " gateway 1.2.3.1", + "line_type": "option", + "option": "gateway", + "value": "1.2.3.1" + } + ], + "iface_options": [ + { + "address_family": "inet", + "iface": "eno1", + "line": " address 1.2.3.5", + "line_type": "option", + "option": "address", + "value": "1.2.3.5" + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " netmask 255.255.255.0", + "line_type": "option", + "option": "netmask", + "value": "255.255.255.0" + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " gateway 1.2.3.1", + "line_type": "option", + "option": "gateway", + "value": "1.2.3.1" + } + ] + }, + } + + for testname in testcases.keys(): + iface_options = interfaces_file.get_interface_options(testcases[testname]["iface_lines"]) + self.assertEqual(testcases[testname]["iface_options"], iface_options) + + def test_get_interface_options(self): + testcases = { + "select address": { + "iface_options": [ + { + "address_family": "inet", + "iface": "eno1", + "line": " address 1.2.3.5", + "line_type": "option", + "option": "address", + "value": "1.2.3.5" + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " netmask 255.255.255.0", + "line_type": "option", + "option": "netmask", + "value": "255.255.255.0" + }, + { + "address_family": "inet", + "iface": "eno1", + "line": " gateway 1.2.3.1", + "line_type": "option", + "option": "gateway", + "value": "1.2.3.1" + } + ], + "target_options": [ + { + "address_family": "inet", + "iface": "eno1", + "line": " address 1.2.3.5", + "line_type": "option", + "option": "address", + "value": "1.2.3.5" + } + ], + "option": "address" + }, + } + + for testname in testcases.keys(): + target_options = interfaces_file.get_target_options(testcases[testname]["iface_options"], testcases[testname]["option"]) + self.assertEqual(testcases[testname]["target_options"], target_options) + + def test_update_existing_option_line(self): + testcases = { + "update address": { + "target_option": { + "address_family": "inet", + "iface": "eno1", + "line": " address 1.2.3.5", + "line_type": "option", + "option": "address", + "value": "1.2.3.5" + }, + "value": "1.2.3.4", + "result": " address 1.2.3.4", + }, + } + + for testname in testcases.keys(): + updated = interfaces_file.update_existing_option_line(testcases[testname]["target_option"], testcases[testname]["value"]) + self.assertEqual(testcases[testname]["result"], updated) + + def test_predefined(self): + testcases = { + "idempotency": { + "source_lines": [ + "iface eno1 inet static", + " address 1.2.3.5", + " netmask 255.255.255.0", + " gateway 1.2.3.1", + ], + "input": { + "iface": "eno1", + "option": "address", + "value": "1.2.3.5", + 'state': 'present', + }, + "result_lines": [ + "iface eno1 inet static", + " address 1.2.3.5", + " netmask 255.255.255.0", + " gateway 1.2.3.1", + ], + "changed": False, + }, + } + + for testname in testcases.keys(): + lines, ifaces = interfaces_file.read_interfaces_lines(module, testcases[testname]["source_lines"]) + changed, lines = interfaces_file.set_interface_option(module, lines, testcases[testname]["input"]['iface'], testcases[testname]["input"]['option'], + testcases[testname]["input"]['value'], testcases[testname]["input"]['state']) + self.assertEqual(testcases[testname]["result_lines"], [d['line'] for d in lines if 'line' in d]) + assert testcases[testname]['changed'] == changed + + def test_inet_inet6(self): + testcases = { + "change_ipv4": [ + { + 'iface': 'eth0', + 'address_family': 'inet', + 'option': 'address', + 'value': '192.168.0.42', + 'state': 'present', + } + ], + "change_ipv6": [ + { + 'iface': 'eth0', + 'address_family': 'inet6', + 'option': 'address', + 'value': 'fc00::42', + 'state': 'present', + } + ], + "change_ipv4_pre_up": [ + { + 'iface': 'eth0', + 'address_family': 'inet', + 'option': 'pre-up', + 'value': 'XXXX_ipv4', + 'state': 'present', + } + ], + "change_ipv6_pre_up": [ + { + 'iface': 'eth0', + 'address_family': 'inet6', + 'option': 'pre-up', + 'value': 'XXXX_ipv6', + 'state': 'present', + } + ], + "change_ipv4_post_up": [ + { + 'iface': 'eth0', + 'address_family': 'inet', + 'option': 'post-up', + 'value': 'XXXX_ipv4', + 'state': 'present', + } + ], + "change_ipv6_post_up": [ + { + 'iface': 'eth0', + 'address_family': 'inet6', + 'option': 'post-up', + 'value': 'XXXX_ipv6', + 'state': 'present', + } + ], + } + for testname, options_list in testcases.items(): + for testfile in self.getTestFiles(): + with tempfile.NamedTemporaryFile() as temp_file: + src_path = os.path.join(fixture_path, testfile) + path = temp_file.name + shutil.copy(src_path, path) + lines, ifaces = interfaces_file.read_interfaces_file(module, path) + backupp = module.backup_local(path) + options = options_list[0] + fail_json_iterations = [] + try: + dummy, lines = interfaces_file.set_interface_option(module, lines, options['iface'], options['option'], + options['value'], options['state'], options['address_family']) + except AnsibleFailJson as e: + fail_json_iterations.append("fail_json message: %s\noptions:\n%s" % + (str(e), json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')))) + interfaces_file.write_changes(module, [d['line'] for d in lines if 'line' in d], path) + + self.compareStringWithFile("\n=====\n".join(fail_json_iterations), "%s_%s.exceptions.txt" % (testfile, testname)) + + self.compareInterfacesLinesToFile(lines, testfile, "%s_%s" % (testfile, testname)) + self.compareInterfacesToFile(ifaces, testfile, "%s_%s.json" % (testfile, testname)) + # Restore backup + move(backupp, path) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/linode_conftest.py b/ansible_collections/community/general/tests/unit/plugins/modules/linode_conftest.py new file mode 100644 index 000000000..33a704d34 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/linode_conftest.py @@ -0,0 +1,87 @@ +# 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 pytest + + +@pytest.fixture +def api_key(monkeypatch): + monkeypatch.setenv('LINODE_API_KEY', 'foobar') + + +@pytest.fixture +def auth(monkeypatch): + def patched_test_echo(dummy): + return [] + monkeypatch.setattr('linode.api.Api.test_echo', patched_test_echo) + + +@pytest.fixture +def access_token(monkeypatch): + monkeypatch.setenv('LINODE_ACCESS_TOKEN', 'barfoo') + + +@pytest.fixture +def no_access_token_in_env(monkeypatch): + try: + monkeypatch.delenv('LINODE_ACCESS_TOKEN') + except KeyError: + pass + + +@pytest.fixture +def default_args(): + return {'state': 'present', 'label': 'foo'} + + +@pytest.fixture +def mock_linode(): + class Linode(): + def delete(self, *args, **kwargs): + pass + + @property + def _raw_json(self): + return { + "alerts": { + "cpu": 90, + "io": 10000, + "network_in": 10, + "network_out": 10, + "transfer_quota": 80 + }, + "backups": { + "enabled": False, + "schedule": { + "day": None, + "window": None, + } + }, + "created": "2018-09-26T08:12:33", + "group": "Foobar Group", + "hypervisor": "kvm", + "id": 10480444, + "image": "linode/centos7", + "ipv4": [ + "130.132.285.233" + ], + "ipv6": "2a82:7e00::h03c:46ff:fe04:5cd2/64", + "label": "lin-foo", + "region": "eu-west", + "specs": { + "disk": 25600, + "memory": 1024, + "transfer": 1000, + "vcpus": 1 + }, + "status": "running", + "tags": [], + "type": "g6-nanode-1", + "updated": "2018-09-26T10:10:14", + "watchdog_enabled": True + } + return Linode() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/oneview_conftest.py b/ansible_collections/community/general/tests/unit/plugins/modules/oneview_conftest.py new file mode 100644 index 000000000..f86543d7c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/oneview_conftest.py @@ -0,0 +1,28 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 + +from mock import Mock, patch +from .oneview_module_loader import ONEVIEW_MODULE_UTILS_PATH +from hpOneView.oneview_client import OneViewClient + + +@pytest.fixture +def mock_ov_client(): + patcher_json_file = patch.object(OneViewClient, 'from_json_file') + client = patcher_json_file.start() + return client.return_value + + +@pytest.fixture +def mock_ansible_module(): + patcher_ansible = patch(ONEVIEW_MODULE_UTILS_PATH + '.AnsibleModule') + patcher_ansible = patcher_ansible.start() + ansible_module = Mock() + patcher_ansible.return_value = ansible_module + return ansible_module diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/oneview_module_loader.py b/ansible_collections/community/general/tests/unit/plugins/modules/oneview_module_loader.py new file mode 100644 index 000000000..ae62d9ced --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/oneview_module_loader.py @@ -0,0 +1,36 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 Mock + +# FIXME: These should be done inside of a fixture so that they're only mocked during +# these unittests +if 'hpOneView' not in sys.modules: + sys.modules['hpOneView'] = Mock() + sys.modules['hpOneView.oneview_client'] = Mock() + +ONEVIEW_MODULE_UTILS_PATH = 'ansible_collections.community.general.plugins.module_utils.oneview' +from ansible_collections.community.general.plugins.module_utils.oneview import ( # noqa: F401, pylint: disable=unused-import + OneViewModuleException, + OneViewModuleTaskError, + OneViewModuleResourceNotFound, + OneViewModuleBase, +) + +from ansible_collections.community.general.plugins.modules.oneview_ethernet_network import EthernetNetworkModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_ethernet_network_info import ( # noqa: F401, pylint: disable=unused-import + EthernetNetworkInfoModule, +) +from ansible_collections.community.general.plugins.modules.oneview_fc_network import FcNetworkModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_fc_network_info import FcNetworkInfoModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_fcoe_network import FcoeNetworkModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_fcoe_network_info import FcoeNetworkInfoModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_network_set import NetworkSetModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_network_set_info import NetworkSetInfoModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_san_manager import SanManagerModule # noqa: F401, pylint: disable=unused-import +from ansible_collections.community.general.plugins.modules.oneview_san_manager_info import SanManagerInfoModule # noqa: F401, pylint: disable=unused-import diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/rhn_conftest.py b/ansible_collections/community/general/tests/unit/plugins/modules/rhn_conftest.py new file mode 100644 index 000000000..acc0e2f22 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/rhn_conftest.py @@ -0,0 +1,35 @@ +# 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.module_utils.six.moves import xmlrpc_client + +import pytest + + +def get_method_name(request_body): + return xmlrpc_client.loads(request_body)[1] + + +@pytest.fixture +def mock_request(request, mocker): + responses = request.getfixturevalue('testcase')['calls'] + module_name = request.module.TESTED_MODULE + + def transport_request(host, handler, request_body, verbose=0): + """Fake request""" + method_name = get_method_name(request_body) + excepted_name, response = responses.pop(0) + if method_name == excepted_name: + if isinstance(response, Exception): + raise response + else: + return response + else: + raise Exception('Expected call: %r, called with: %r' % (excepted_name, method_name)) + + target = '{0}.xmlrpc_client.Transport.request'.format(module_name) + mocker.patch(target, side_effect=transport_request) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_alerta_customer.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_alerta_customer.py new file mode 100644 index 000000000..ccd0ced50 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_alerta_customer.py @@ -0,0 +1,250 @@ +# 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 +import pytest +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.plugins.modules import alerta_customer +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class MockedReponse(object): + def __init__(self, data): + self.data = data + + def read(self): + return self.data + + +def customer_response_page1(): + server_response = json.dumps({"customers": [ + { + "customer": "admin", + "href": "http://localhost:8080/api/customer/d89664a7-9c87-4ab9-8be8-830e7e5f0616", + "id": "d89664a7-9c87-4ab9-8be8-830e7e5f0616", + "match": "admin@example.com" + }, + { + "customer": "Developer", + "href": "http://localhost:8080/api/customer/188ed093-84cc-4f46-bf80-4c9127180d9c", + "id": "188ed093-84cc-4f46-bf80-4c9127180d9c", + "match": "dev@example.com" + }], + "more": True, + "page": 1, + "pageSize": 50, + "pages": 1, + "status": "ok", + "total": 2}) + return (MockedReponse(server_response), {"status": 200}) + + +def customer_response_page2(): + server_response = json.dumps({"customers": [ + { + "customer": "admin", + "href": "http://localhost:8080/api/customer/d89664a7-9c87-4ab9-8be8-830e7e5f0616", + "id": "d89664a7-9c87-4ab9-8be8-830e7e5f0616", + "match": "admin@example.com" + }, + { + "customer": "Developer", + "href": "http://localhost:8080/api/customer/188ed093-84cc-4f46-bf80-4c9127180d9c", + "id": "188ed093-84cc-4f46-bf80-4c9127180d9c", + "match": "dev@example.com" + }], + "more": True, + "page": 2, + "pageSize": 50, + "pages": 2, + "status": "ok", + "total": 52}) + return (MockedReponse(server_response), {"status": 200}) + + +class TestAlertaCustomerModule(ModuleTestCase): + + def setUp(self): + super(TestAlertaCustomerModule, self).setUp() + self.module = alerta_customer + + def tearDown(self): + super(TestAlertaCustomerModule, self).tearDown() + + @pytest.fixture + def fetch_url_mock(self, mocker): + return mocker.patch() + + def test_without_parameters(self): + """Failure if no parameters set""" + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + self.module.main() + + def test_without_content(self): + """Failure if customer and match are missing""" + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password" + }) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_successful_existing_customer_creation(self): + """Test the customer creation (already exists).""" + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password", + 'customer': 'Developer', + 'match': 'dev@example.com' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = customer_response_page1() + with self.assertRaises(AnsibleExitJson): + self.module.main() + self.assertTrue(fetch_url_mock.call_count, 1) + + def test_successful_customer_creation(self): + """Test the customer creation.""" + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password", + 'customer': 'Developer', + 'match': 'dev2@example.com' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = customer_response_page1() + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['match'] == "dev2@example.com" + assert call_data['customer'] == "Developer" + + def test_successful_customer_creation_key(self): + """Test the customer creation using api_key.""" + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_key': "demo-key", + 'customer': 'Developer', + 'match': 'dev2@example.com' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = customer_response_page1() + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['match'] == "dev2@example.com" + assert call_data['customer'] == "Developer" + + def test_failed_not_found(self): + """Test failure with wrong URL.""" + + set_module_args({ + 'alerta_url': "http://localhost:8080/s", + 'api_username': "admin@example.com", + 'api_password': "password", + 'customer': 'Developer', + 'match': 'dev@example.com' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'Not found for request GET on http://localhost:8080/a/api/customers'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_failed_forbidden(self): + """Test failure with wrong user.""" + + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "dev@example.com", + 'api_password': "password", + 'customer': 'Developer', + 'match': 'dev@example.com' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 403, 'msg': 'Permission Denied for GET on http://localhost:8080/api/customers'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_failed_unauthorized(self): + """Test failure with wrong username or password.""" + + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password_wrong", + 'customer': 'Developer', + 'match': 'dev@example.com' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 401, 'msg': 'Unauthorized to request GET on http://localhost:8080/api/customers'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_successful_customer_deletion(self): + """Test the customer deletion.""" + + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password", + 'customer': 'Developer', + 'match': 'dev@example.com', + 'state': 'absent' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = customer_response_page1() + with self.assertRaises(AnsibleExitJson): + self.module.main() + + def test_successful_customer_deletion_page2(self): + """Test the customer deletion on the second page.""" + + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password", + 'customer': 'Developer', + 'match': 'dev@example.com', + 'state': 'absent' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = customer_response_page2() + with self.assertRaises(AnsibleExitJson): + self.module.main() + + def test_successful_nonexisting_customer_deletion(self): + """Test the customer deletion (non existing).""" + + set_module_args({ + 'alerta_url': "http://localhost:8080", + 'api_username': "admin@example.com", + 'api_password': "password", + 'customer': 'Billing', + 'match': 'dev@example.com', + 'state': 'absent' + }) + + with patch.object(alerta_customer, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = customer_response_page1() + with self.assertRaises(AnsibleExitJson): + self.module.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_apache2_module.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_apache2_module.py new file mode 100644 index 000000000..3e44bdb58 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_apache2_module.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible_collections.community.general.plugins.modules.apache2_module import create_apache_identifier + +REPLACEMENTS = [ + ('php7.1', 'php7_module'), + ('php5.6', 'php5_module'), + ('shib2', 'mod_shib'), + ('evasive', 'evasive20_module'), + ('thismoduledoesnotexist', 'thismoduledoesnotexist_module'), # the default +] + + +@pytest.mark.parametrize("replacement", REPLACEMENTS, ids=lambda x: x[0]) +def test_apache_identifier(replacement): + "test the correct replacement of an a2enmod name with an apache2ctl name" + assert create_apache_identifier(replacement[0]) == replacement[1] diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_apk.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_apk.py new file mode 100644 index 000000000..c952456ef --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_apk.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.community.general.tests.unit.compat import mock +from ansible_collections.community.general.tests.unit.compat import unittest + +from ansible_collections.community.general.plugins.modules import apk + + +class TestApkQueryLatest(unittest.TestCase): + + def setUp(self): + self.module_names = [ + 'bash', + 'g++', + ] + + @mock.patch('ansible_collections.community.general.plugins.modules.apk.AnsibleModule') + def test_not_latest(self, mock_module): + apk.APK_PATH = "" + for module_name in self.module_names: + command_output = module_name + '-2.0.0-r1 < 3.0.0-r2 ' + mock_module.run_command.return_value = (0, command_output, None) + command_result = apk.query_latest(mock_module, module_name) + self.assertFalse(command_result) + + @mock.patch('ansible_collections.community.general.plugins.modules.apk.AnsibleModule') + def test_latest(self, mock_module): + apk.APK_PATH = "" + for module_name in self.module_names: + command_output = module_name + '-2.0.0-r1 = 2.0.0-r1 ' + mock_module.run_command.return_value = (0, command_output, None) + command_result = apk.query_latest(mock_module, module_name) + self.assertTrue(command_result) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_archive.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_archive.py new file mode 100644 index 000000000..84a1360f1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_archive.py @@ -0,0 +1,75 @@ +# -*- 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 pytest + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.tests.unit.compat.mock import Mock, patch +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ModuleTestCase, set_module_args +from ansible_collections.community.general.plugins.modules.archive import get_archive, common_path + + +class TestArchive(ModuleTestCase): + def setUp(self): + super(TestArchive, self).setUp() + + self.mock_os_path_isdir = patch('os.path.isdir') + self.os_path_isdir = self.mock_os_path_isdir.start() + + def tearDown(self): + self.os_path_isdir = self.mock_os_path_isdir.stop() + + def test_archive_removal_safety(self): + set_module_args( + dict( + path=['/foo', '/bar', '/baz'], + dest='/foo/destination.tgz', + remove=True + ) + ) + + module = AnsibleModule( + argument_spec=dict( + path=dict(type='list', elements='path', required=True), + format=dict(type='str', default='gz', choices=['bz2', 'gz', 'tar', 'xz', 'zip']), + dest=dict(type='path'), + exclude_path=dict(type='list', elements='path', default=[]), + exclusion_patterns=dict(type='list', elements='path'), + force_archive=dict(type='bool', default=False), + remove=dict(type='bool', default=False), + ), + add_file_common_args=True, + supports_check_mode=True, + ) + + self.os_path_isdir.side_effect = [True, False, False, True] + + module.fail_json = Mock() + + archive = get_archive(module) + + module.fail_json.assert_called_once_with( + path=b', '.join(archive.paths), + msg='Error, created archive can not be contained in source paths when remove=true' + ) + + +PATHS = ( + ([], ''), + (['/'], '/'), + ([b'/'], b'/'), + (['/foo', '/bar', '/baz', '/foobar', '/barbaz', '/foo/bar'], '/'), + ([b'/foo', b'/bar', b'/baz', b'/foobar', b'/barbaz', b'/foo/bar'], b'/'), + (['/foo/bar/baz', '/foo/bar'], '/foo/'), + (['/foo/bar/baz', '/foo/bar/'], '/foo/bar/'), +) + + +@pytest.mark.parametrize("paths,root", PATHS) +def test_common_path(paths, root): + assert common_path(paths) == root diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_access_key.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_access_key.py new file mode 100644 index 000000000..71e28f653 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_access_key.py @@ -0,0 +1,343 @@ +# 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.plugins.module_utils.source_control.bitbucket import BitbucketHelper +from ansible_collections.community.general.plugins.modules import bitbucket_access_key +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 AnsibleFailJson, AnsibleExitJson, ModuleTestCase, set_module_args + + +class TestBucketAccessKeyModule(ModuleTestCase): + def setUp(self): + super(TestBucketAccessKeyModule, self).setUp() + self.module = bitbucket_access_key + + def test_missing_key_with_present_state(self): + with self.assertRaises(AnsibleFailJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'label': 'key name', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['msg'], self.module.error_messages['required_key']) + + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value=None) + def test_create_deploy_key(self, *args): + with patch.object(self.module, 'create_deploy_key') as create_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'user': 'ABC', + 'password': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'key': 'public_key', + 'label': 'key name', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(create_deploy_key_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value=None) + def test_create_deploy_key_check_mode(self, *args): + with patch.object(self.module, 'create_deploy_key') as create_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'key': 'public_key', + 'label': 'key name', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(create_deploy_key_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value={ + "id": 123, + "label": "mykey", + "created_on": "2019-03-23T10:15:21.517377+00:00", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADA...AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5", + "type": "deploy_key", + "comment": "", + "last_used": None, + "repository": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test" + }, + "html": { + "href": "https://bitbucket.org/mleu/test" + }, + "avatar": { + "href": "..." + } + }, + "type": "repository", + "name": "test", + "full_name": "mleu/test", + "uuid": "{85d08b4e-571d-44e9-a507-fa476535aa98}" + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123" + } + }, + }) + def test_update_deploy_key(self, *args): + with patch.object(self.module, 'delete_deploy_key') as delete_deploy_key_mock: + with patch.object(self.module, 'create_deploy_key') as create_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'key': 'new public key', + 'label': 'mykey', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(delete_deploy_key_mock.call_count, 1) + self.assertEqual(create_deploy_key_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value={ + "id": 123, + "label": "mykey", + "created_on": "2019-03-23T10:15:21.517377+00:00", + "key": "new public key", + "type": "deploy_key", + "comment": "", + "last_used": None, + "repository": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test" + }, + "html": { + "href": "https://bitbucket.org/mleu/test" + }, + "avatar": { + "href": "..." + } + }, + "type": "repository", + "name": "test", + "full_name": "mleu/test", + "uuid": "{85d08b4e-571d-44e9-a507-fa476535aa98}" + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123" + } + }, + }) + def test_dont_update_same_value(self, *args): + with patch.object(self.module, 'delete_deploy_key') as delete_deploy_key_mock: + with patch.object(self.module, 'create_deploy_key') as create_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'key': 'new public key', + 'label': 'mykey', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(delete_deploy_key_mock.call_count, 0) + self.assertEqual(create_deploy_key_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value={ + "id": 123, + "label": "mykey", + "created_on": "2019-03-23T10:15:21.517377+00:00", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADA...AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5", + "type": "deploy_key", + "comment": "", + "last_used": None, + "repository": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test" + }, + "html": { + "href": "https://bitbucket.org/mleu/test" + }, + "avatar": { + "href": "..." + } + }, + "type": "repository", + "name": "test", + "full_name": "mleu/test", + "uuid": "{85d08b4e-571d-44e9-a507-fa476535aa98}" + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123" + } + }, + }) + def test_update_deploy_key_check_mode(self, *args): + with patch.object(self.module, 'delete_deploy_key') as delete_deploy_key_mock: + with patch.object(self.module, 'create_deploy_key') as create_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'key': 'new public key', + 'label': 'mykey', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(delete_deploy_key_mock.call_count, 0) + self.assertEqual(create_deploy_key_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value={ + "id": 123, + "label": "mykey", + "created_on": "2019-03-23T10:15:21.517377+00:00", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADA...AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5", + "type": "deploy_key", + "comment": "", + "last_used": None, + "repository": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test" + }, + "html": { + "href": "https://bitbucket.org/mleu/test" + }, + "avatar": { + "href": "..." + } + }, + "type": "repository", + "name": "test", + "full_name": "mleu/test", + "uuid": "{85d08b4e-571d-44e9-a507-fa476535aa98}" + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123" + } + }, + }) + def test_delete_deploy_key(self, *args): + with patch.object(self.module, 'delete_deploy_key') as delete_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'label': 'mykey', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_deploy_key_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value=None) + def test_delete_absent_deploy_key(self, *args): + with patch.object(self.module, 'delete_deploy_key') as delete_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'label': 'mykey', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_deploy_key_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_access_key, 'get_existing_deploy_key', return_value={ + "id": 123, + "label": "mykey", + "created_on": "2019-03-23T10:15:21.517377+00:00", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADA...AdkTg7HGqL3rlaDrEcWfL7Lu6TnhBdq5", + "type": "deploy_key", + "comment": "", + "last_used": None, + "repository": { + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test" + }, + "html": { + "href": "https://bitbucket.org/mleu/test" + }, + "avatar": { + "href": "..." + } + }, + "type": "repository", + "name": "test", + "full_name": "mleu/test", + "uuid": "{85d08b4e-571d-44e9-a507-fa476535aa98}" + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/mleu/test/deploy-keys/123" + } + }, + }) + def test_delete_deploy_key_check_mode(self, *args): + with patch.object(self.module, 'delete_deploy_key') as delete_deploy_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'label': 'mykey', + 'state': 'absent', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(delete_deploy_key_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_key_pair.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_key_pair.py new file mode 100644 index 000000000..a1f5478c2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_key_pair.py @@ -0,0 +1,198 @@ +# 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.plugins.module_utils.source_control.bitbucket import BitbucketHelper +from ansible_collections.community.general.plugins.modules import bitbucket_pipeline_key_pair +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 AnsibleFailJson, AnsibleExitJson, ModuleTestCase, set_module_args + + +class TestBucketPipelineKeyPairModule(ModuleTestCase): + def setUp(self): + super(TestBucketPipelineKeyPairModule, self).setUp() + self.module = bitbucket_pipeline_key_pair + + def test_missing_keys_with_present_state(self): + with self.assertRaises(AnsibleFailJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['msg'], self.module.error_messages['required_keys']) + + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value=None) + def test_create_keys(self, *args): + with patch.object(self.module, 'update_ssh_key_pair') as update_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'user': 'ABC', + 'password': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'public_key': 'public', + 'private_key': 'PRIVATE', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_ssh_key_pair_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value=None) + def test_create_keys_check_mode(self, *args): + with patch.object(self.module, 'update_ssh_key_pair') as update_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'public_key': 'public', + 'private_key': 'PRIVATE', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(update_ssh_key_pair_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value={ + 'public_key': 'unknown', + 'type': 'pipeline_ssh_key_pair', + }) + def test_update_keys(self, *args): + with patch.object(self.module, 'update_ssh_key_pair') as update_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'public_key': 'public', + 'private_key': 'PRIVATE', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_ssh_key_pair_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value={ + 'public_key': 'public', + 'type': 'pipeline_ssh_key_pair', + }) + def test_dont_update_same_key(self, *args): + with patch.object(self.module, 'update_ssh_key_pair') as update_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'public_key': 'public', + 'private_key': 'PRIVATE', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_ssh_key_pair_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value={ + 'public_key': 'unknown', + 'type': 'pipeline_ssh_key_pair', + }) + def test_update_keys_check_mode(self, *args): + with patch.object(self.module, 'update_ssh_key_pair') as update_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'public_key': 'public', + 'private_key': 'PRIVATE', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(update_ssh_key_pair_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value={ + 'public_key': 'public', + 'type': 'pipeline_ssh_key_pair', + }) + def test_delete_keys(self, *args): + with patch.object(self.module, 'delete_ssh_key_pair') as delete_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_ssh_key_pair_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value=None) + def test_delete_absent_keys(self, *args): + with patch.object(self.module, 'delete_ssh_key_pair') as delete_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_ssh_key_pair_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_key_pair, 'get_existing_ssh_key_pair', return_value={ + 'public_key': 'public', + 'type': 'pipeline_ssh_key_pair', + }) + def test_delete_keys_check_mode(self, *args): + with patch.object(self.module, 'delete_ssh_key_pair') as delete_ssh_key_pair_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'state': 'absent', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(delete_ssh_key_pair_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_known_host.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_known_host.py new file mode 100644 index 000000000..07709f1a8 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_known_host.py @@ -0,0 +1,193 @@ +# 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 pytest + +from ansible_collections.community.general.plugins.module_utils.source_control.bitbucket import BitbucketHelper +from ansible_collections.community.general.plugins.modules import bitbucket_pipeline_known_host +from ansible_collections.community.general.plugins.modules.bitbucket_pipeline_known_host import HAS_PARAMIKO +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 + + +class TestBucketPipelineKnownHostModule(ModuleTestCase): + def setUp(self): + super(TestBucketPipelineKnownHostModule, self).setUp() + self.module = bitbucket_pipeline_known_host + + @pytest.mark.skipif(not HAS_PARAMIKO, reason='paramiko must be installed to test key creation') + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value=None) + def test_create_known_host(self, *args): + with patch.object(self.module, 'create_known_host') as create_known_host_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(create_known_host_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'request', return_value=(dict(status=201), dict())) + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value=None) + def test_create_known_host_with_key(self, *args): + with patch.object(self.module, 'get_host_key') as get_host_key_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'user': 'ABC', + 'password': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'key': 'ssh-rsa public', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(get_host_key_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @pytest.mark.skipif(not HAS_PARAMIKO, reason='paramiko must be installed to test key creation') + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value={ + 'type': 'pipeline_known_host', + 'uuid': '{21cc0590-bebe-4fae-8baf-03722704119a7}', + 'hostname': 'bitbucket.org', + 'public_key': { + 'type': 'pipeline_ssh_public_key', + 'md5_fingerprint': 'md5:97:8c:1b:f2:6f:14:6b:4b:3b:ec:aa:46:46:74:7c:40', + 'sha256_fingerprint': 'SHA256:zzXQOXSFBEiUtuE8AikoYKwbHaxvSc0ojez9YXaGp1A', + 'key_type': 'ssh-rsa', + 'key': 'AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kN...seeFVBoGqzHM9yXw==' + } + }) + def test_dont_create_same_value(self, *args): + with patch.object(self.module, 'create_known_host') as create_known_host_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(create_known_host_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @pytest.mark.skipif(not HAS_PARAMIKO, reason='paramiko must be installed to test key creation') + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value=None) + def test_create_known_host_check_mode(self, *args): + with patch.object(self.module, 'create_known_host') as create_known_host_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(create_known_host_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @pytest.mark.skipif(not HAS_PARAMIKO, reason='paramiko must be installed to test key creation') + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value={ + 'type': 'pipeline_known_host', + 'uuid': '{21cc0590-bebe-4fae-8baf-03722704119a7}', + 'hostname': 'bitbucket.org', + 'public_key': { + 'type': 'pipeline_ssh_public_key', + 'md5_fingerprint': 'md5:97:8c:1b:f2:6f:14:6b:4b:3b:ec:aa:46:46:74:7c:40', + 'sha256_fingerprint': 'SHA256:zzXQOXSFBEiUtuE8AikoYKwbHaxvSc0ojez9YXaGp1A', + 'key_type': 'ssh-rsa', + 'key': 'AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kN...seeFVBoGqzHM9yXw==' + } + }) + def test_delete_known_host(self, *args): + with patch.object(self.module, 'delete_known_host') as delete_known_host_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_known_host_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @pytest.mark.skipif(not HAS_PARAMIKO, reason='paramiko must be installed to test key creation') + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value=None) + def test_delete_absent_known_host(self, *args): + with patch.object(self.module, 'delete_known_host') as delete_known_host_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_known_host_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @pytest.mark.skipif(not HAS_PARAMIKO, reason='paramiko must be installed to test key creation') + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_known_host, 'get_existing_known_host', return_value={ + 'type': 'pipeline_known_host', + 'uuid': '{21cc0590-bebe-4fae-8baf-03722704119a7}', + 'hostname': 'bitbucket.org', + 'public_key': { + 'type': 'pipeline_ssh_public_key', + 'md5_fingerprint': 'md5:97:8c:1b:f2:6f:14:6b:4b:3b:ec:aa:46:46:74:7c:40', + 'sha256_fingerprint': 'SHA256:zzXQOXSFBEiUtuE8AikoYKwbHaxvSc0ojez9YXaGp1A', + 'key_type': 'ssh-rsa', + 'key': 'AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kN...seeFVBoGqzHM9yXw==' + } + }) + def test_delete_known_host_check_mode(self, *args): + with patch.object(self.module, 'delete_known_host') as delete_known_host_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'bitbucket.org', + 'state': 'absent', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(delete_known_host_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_variable.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_variable.py new file mode 100644 index 000000000..6f710189c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_bitbucket_pipeline_variable.py @@ -0,0 +1,311 @@ +# 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.plugins.module_utils.source_control.bitbucket import BitbucketHelper +from ansible_collections.community.general.plugins.modules import bitbucket_pipeline_variable +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 AnsibleFailJson, AnsibleExitJson, ModuleTestCase, set_module_args + + +class TestBucketPipelineVariableModule(ModuleTestCase): + def setUp(self): + super(TestBucketPipelineVariableModule, self).setUp() + self.module = bitbucket_pipeline_variable + + def test_without_required_parameters(self): + with self.assertRaises(AnsibleFailJson) as exec_info: + set_module_args({ + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['failed'], True) + + def test_missing_value_with_present_state(self): + with self.assertRaises(AnsibleFailJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['msg'], self.module.error_messages['required_value']) + + @patch.dict('os.environ', { + 'BITBUCKET_CLIENT_ID': 'ABC', + 'BITBUCKET_CLIENT_SECRET': 'XXX', + }) + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value=None) + def test_oauth_env_vars_params(self, *args): + with self.assertRaises(AnsibleExitJson): + set_module_args({ + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'absent', + }) + self.module.main() + + @patch.dict('os.environ', { + 'BITBUCKET_USERNAME': 'ABC', + 'BITBUCKET_PASSWORD': 'XXX', + }) + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value=None) + def test_basic_auth_env_vars_params(self, *args): + with self.assertRaises(AnsibleExitJson): + set_module_args({ + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'absent', + }) + self.module.main() + + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value=None) + def test_create_variable(self, *args): + with patch.object(self.module, 'create_pipeline_variable') as create_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'user': 'ABC', + 'password': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(create_pipeline_variable_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value=None) + def test_create_variable_check_mode(self, *args): + with patch.object(self.module, 'create_pipeline_variable') as create_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(create_pipeline_variable_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'value': 'Im alive', + 'type': 'pipeline_variable', + 'secured': False, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_update_variable(self, *args): + with patch.object(self.module, 'update_pipeline_variable') as update_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_pipeline_variable_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'type': 'pipeline_variable', + 'secured': True, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_update_secured_variable(self, *args): + with patch.object(self.module, 'update_pipeline_variable') as update_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'secured': True, + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_pipeline_variable_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'type': 'pipeline_variable', + 'secured': False, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_update_secured_state(self, *args): + with patch.object(self.module, 'update_pipeline_variable') as update_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'secured': True, + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_pipeline_variable_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'type': 'pipeline_variable', + 'secured': False, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_dont_update_same_value(self, *args): + with patch.object(self.module, 'update_pipeline_variable') as update_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'state': 'present', + }) + self.module.main() + + self.assertEqual(update_pipeline_variable_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'value': 'Im alive', + 'type': 'pipeline_variable', + 'secured': False, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_update_variable_check_mode(self, *args): + with patch.object(self.module, 'update_pipeline_variable') as update_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'value': '42', + 'state': 'present', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(update_pipeline_variable_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'value': 'Im alive', + 'type': 'pipeline_variable', + 'secured': False, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_delete_variable(self, *args): + with patch.object(self.module, 'delete_pipeline_variable') as delete_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_pipeline_variable_mock.call_count, 1) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value=None) + def test_delete_absent_variable(self, *args): + with patch.object(self.module, 'delete_pipeline_variable') as delete_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'absent', + }) + self.module.main() + + self.assertEqual(delete_pipeline_variable_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], False) + + @patch.object(BitbucketHelper, 'fetch_access_token', return_value='token') + @patch.object(bitbucket_pipeline_variable, 'get_existing_pipeline_variable', return_value={ + 'name': 'PIPELINE_VAR_NAME', + 'value': 'Im alive', + 'type': 'pipeline_variable', + 'secured': False, + 'uuid': '{9ddb0507-439a-495a- 99f3 - 564f15138127}' + }) + def test_delete_variable_check_mode(self, *args): + with patch.object(self.module, 'delete_pipeline_variable') as delete_pipeline_variable_mock: + with self.assertRaises(AnsibleExitJson) as exec_info: + set_module_args({ + 'client_id': 'ABC', + 'client_secret': 'XXX', + 'workspace': 'name', + 'repository': 'repo', + 'name': 'PIPELINE_VAR_NAME', + 'state': 'absent', + '_ansible_check_mode': True, + }) + self.module.main() + + self.assertEqual(delete_pipeline_variable_mock.call_count, 0) + self.assertEqual(exec_info.exception.args[0]['changed'], True) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_campfire.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_campfire.py new file mode 100644 index 000000000..ef0dca5ed --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_campfire.py @@ -0,0 +1,96 @@ +# 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 pytest +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.plugins.modules import campfire +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class TestCampfireModule(ModuleTestCase): + + def setUp(self): + super(TestCampfireModule, self).setUp() + self.module = campfire + + def tearDown(self): + super(TestCampfireModule, self).tearDown() + + @pytest.fixture + def fetch_url_mock(self, mocker): + return mocker.patch('ansible.module_utils.notification.campfire.fetch_url') + + 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_successful_message(self): + """Test failure message""" + set_module_args({ + 'subscription': 'test', + 'token': 'abc', + 'room': 'test', + 'msg': 'test' + }) + + with patch.object(campfire, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 200}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + assert fetch_url_mock.call_count == 1 + url = fetch_url_mock.call_args[0][1] + data = fetch_url_mock.call_args[1]['data'] + + assert url == 'https://test.campfirenow.com/room/test/speak.xml' + assert data == 'test' + + def test_successful_message_with_notify(self): + """Test failure message""" + set_module_args({ + 'subscription': 'test', + 'token': 'abc', + 'room': 'test', + 'msg': 'test', + 'notify': 'bell' + }) + + with patch.object(campfire, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 200}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + assert fetch_url_mock.call_count == 2 + notify_call = fetch_url_mock.mock_calls[0] + url = notify_call[1][1] + data = notify_call[2]['data'] + + assert url == 'https://test.campfirenow.com/room/test/speak.xml' + assert data == 'SoundMessagebell' + + message_call = fetch_url_mock.mock_calls[1] + url = message_call[1][1] + data = message_call[2]['data'] + + assert url == 'https://test.campfirenow.com/room/test/speak.xml' + assert data == 'test' + + def test_failure_message(self): + """Test failure message""" + set_module_args({ + 'subscription': 'test', + 'token': 'abc', + 'room': 'test', + 'msg': 'test' + }) + + with patch.object(campfire, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 403}) + with self.assertRaises(AnsibleFailJson): + self.module.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_circonus_annotation.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_circonus_annotation.py new file mode 100644 index 000000000..7378e62a2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_circonus_annotation.py @@ -0,0 +1,153 @@ +# -*- 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 io +import json +import re +import uuid +from urllib3.response import HTTPResponse + +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible.module_utils.common.text.converters import to_bytes +from ansible_collections.community.general.plugins.modules import circonus_annotation +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class TestCirconusAnnotation(ModuleTestCase): + + def setUp(self): + super(TestCirconusAnnotation, self).setUp() + self.module = circonus_annotation + + def tearDown(self): + super(TestCirconusAnnotation, 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_add_annotation(self): + """Check that result is changed""" + set_module_args({ + 'category': 'test category', + 'description': 'test description', + 'title': 'test title', + 'api_key': str(uuid.uuid4()), + }) + + cid = '/annotation/100000' + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + data = { + '_cid': cid, + '_created': 1502146995, + '_last_modified': 1502146995, + '_last_modified_by': '/user/1000', + 'category': 'test category', + 'description': 'test description', + 'rel_metrics': [], + 'start': 1502145480, + 'stop': None, + 'title': 'test title', + } + raw = to_bytes(json.dumps(data)) + resp = HTTPResponse(body=io.BytesIO(raw), preload_content=False) + resp.status = 200 + resp.reason = 'OK' + resp.headers = {'X-Circonus-API-Version': '2.00'} + return self.build_response(request, resp) + + with patch('requests.adapters.HTTPAdapter.send', autospec=True, side_effect=send) as send: + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + self.assertEqual(result.exception.args[0]['annotation']['_cid'], cid) + self.assertEqual(send.call_count, 1) + + def test_add_annotation_unicode(self): + """Check that result is changed. + Note: it seems there is a bug which prevent to create an annotation + with a non-ASCII category if this category already exists, in such + case an Internal Server Error (500) occurs.""" + set_module_args({ + 'category': 'new catégorÿ', + 'description': 'test description', + 'title': 'test title', + 'api_key': str(uuid.uuid4()), + }) + + cid = '/annotation/100000' + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + data = { + '_cid': '/annotation/100000', + '_created': 1502236928, + '_last_modified': 1502236928, + '_last_modified_by': '/user/1000', + # use res['annotation']['category'].encode('latin1').decode('utf8') + 'category': u'new cat\xc3\xa9gor\xc3\xbf', + 'description': 'test description', + 'rel_metrics': [], + 'start': 1502236927, + 'stop': 1502236927, + 'title': 'test title', + } + + raw = to_bytes(json.dumps(data), encoding='latin1') + resp = HTTPResponse(body=io.BytesIO(raw), preload_content=False) + resp.status = 200 + resp.reason = 'OK' + resp.headers = {'X-Circonus-API-Version': '2.00'} + return self.build_response(request, resp) + + with patch('requests.adapters.HTTPAdapter.send', autospec=True, side_effect=send) as send: + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + self.assertEqual(result.exception.args[0]['annotation']['_cid'], cid) + self.assertEqual(send.call_count, 1) + + def test_auth_failure(self): + """Check that an error is raised when authentication failed""" + set_module_args({ + 'category': 'test category', + 'description': 'test description', + 'title': 'test title', + 'api_key': str(uuid.uuid4()), + }) + + cid = '/annotation/100000' + + def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): + data = { + '_cid': cid, + '_created': 1502146995, + '_last_modified': 1502146995, + '_last_modified_by': '/user/1000', + 'category': 'test category', + 'description': 'test description', + 'rel_metrics': [], + 'start': 1502145480, + 'stop': None, + 'title': 'test title', + } + raw = to_bytes(json.dumps(data)) + resp = HTTPResponse(body=io.BytesIO(raw), preload_content=False) + resp.status = 403 + resp.reason = 'Forbidden' + resp.headers = {'X-Circonus-API-Version': '2.00'} + return self.build_response(request, resp) + + with patch('requests.adapters.HTTPAdapter.send', autospec=True, side_effect=send) as send: + with self.assertRaises(AnsibleFailJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['failed']) + self.assertTrue(re.match(r'\b403\b', result.exception.args[0]['reason'])) + self.assertEqual(send.call_count, 1) 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 new file mode 100644 index 000000000..5367a1fab --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py @@ -0,0 +1,293 @@ +# -*- coding: utf-8 -*- +# Author: Alexei Znamensky (russoz@gmail.com) +# Largely adapted from test_redhat_subscription by +# Jiri Hnidek (jhnidek@redhat.com) +# +# Copyright (c) Alexei Znamensky (russoz@gmail.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 json + +from ansible_collections.community.general.plugins.modules import cpanm + +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'] diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_datadog_downtime.py.disabled b/ansible_collections/community/general/tests/unit/plugins/modules/test_datadog_downtime.py.disabled new file mode 100644 index 000000000..52f27710c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_datadog_downtime.py.disabled @@ -0,0 +1,226 @@ +# -*- 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 + +from ansible_collections.community.general.plugins.modules.monitoring.datadog import datadog_downtime +from ansible_collections.community.general.tests.unit.compat.mock import MagicMock, patch +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args +) + +from pytest import importorskip + +# Skip this test if python 2 so datadog_api_client cannot be installed +datadog_api_client = importorskip("datadog_api_client") +Downtime = datadog_api_client.v1.model.downtime.Downtime +DowntimeRecurrence = datadog_api_client.v1.model.downtime_recurrence.DowntimeRecurrence + + +class TestDatadogDowntime(ModuleTestCase): + + def setUp(self): + super(TestDatadogDowntime, self).setUp() + self.module = datadog_downtime + + def tearDown(self): + super(TestDatadogDowntime, 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() + + @patch("ansible_collections.community.general.plugins.modules.monitoring.datadog.datadog_downtime.DowntimesApi") + def test_create_downtime_when_no_id(self, downtimes_api_mock): + set_module_args({ + "monitor_tags": ["foo:bar"], + "scope": ["*"], + "monitor_id": 12345, + "downtime_message": "Message", + "start": 1111, + "end": 2222, + "timezone": "UTC", + "rrule": "rrule", + "api_key": "an_api_key", + "app_key": "an_app_key", + }) + + downtime = Downtime() + downtime.monitor_tags = ["foo:bar"] + downtime.scope = ["*"] + downtime.monitor_id = 12345 + downtime.message = "Message" + downtime.start = 1111 + downtime.end = 2222 + downtime.timezone = "UTC" + downtime.recurrence = DowntimeRecurrence( + rrule="rrule" + ) + + create_downtime_mock = MagicMock(return_value=Downtime(id=12345)) + downtimes_api_mock.return_value = MagicMock(create_downtime=create_downtime_mock) + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + self.assertEqual(result.exception.args[0]['downtime']['id'], 12345) + create_downtime_mock.assert_called_once_with(downtime) + + @patch("ansible_collections.community.general.plugins.modules.monitoring.datadog.datadog_downtime.DowntimesApi") + def test_create_downtime_when_id_and_disabled(self, downtimes_api_mock): + set_module_args({ + "id": 1212, + "monitor_tags": ["foo:bar"], + "scope": ["*"], + "monitor_id": 12345, + "downtime_message": "Message", + "start": 1111, + "end": 2222, + "timezone": "UTC", + "rrule": "rrule", + "api_key": "an_api_key", + "app_key": "an_app_key", + }) + + downtime = Downtime() + downtime.monitor_tags = ["foo:bar"] + downtime.scope = ["*"] + downtime.monitor_id = 12345 + downtime.message = "Message" + downtime.start = 1111 + downtime.end = 2222 + downtime.timezone = "UTC" + downtime.recurrence = DowntimeRecurrence( + rrule="rrule" + ) + + create_downtime_mock = MagicMock(return_value=Downtime(id=12345)) + get_downtime_mock = MagicMock(return_value=Downtime(id=1212, disabled=True)) + downtimes_api_mock.return_value = MagicMock( + create_downtime=create_downtime_mock, get_downtime=get_downtime_mock + ) + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + self.assertEqual(result.exception.args[0]['downtime']['id'], 12345) + create_downtime_mock.assert_called_once_with(downtime) + get_downtime_mock.assert_called_once_with(1212) + + @patch("ansible_collections.community.general.plugins.modules.monitoring.datadog.datadog_downtime.DowntimesApi") + def test_update_downtime_when_not_disabled(self, downtimes_api_mock): + set_module_args({ + "id": 1212, + "monitor_tags": ["foo:bar"], + "scope": ["*"], + "monitor_id": 12345, + "downtime_message": "Message", + "start": 1111, + "end": 2222, + "timezone": "UTC", + "rrule": "rrule", + "api_key": "an_api_key", + "app_key": "an_app_key", + }) + + downtime = Downtime() + downtime.monitor_tags = ["foo:bar"] + downtime.scope = ["*"] + downtime.monitor_id = 12345 + downtime.message = "Message" + downtime.start = 1111 + downtime.end = 2222 + downtime.timezone = "UTC" + downtime.recurrence = DowntimeRecurrence( + rrule="rrule" + ) + + update_downtime_mock = MagicMock(return_value=Downtime(id=1212)) + get_downtime_mock = MagicMock(return_value=Downtime(id=1212, disabled=False)) + downtimes_api_mock.return_value = MagicMock( + update_downtime=update_downtime_mock, get_downtime=get_downtime_mock + ) + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + self.assertEqual(result.exception.args[0]['downtime']['id'], 1212) + update_downtime_mock.assert_called_once_with(1212, downtime) + get_downtime_mock.assert_called_once_with(1212) + + @patch("ansible_collections.community.general.plugins.modules.monitoring.datadog.datadog_downtime.DowntimesApi") + def test_update_downtime_no_change(self, downtimes_api_mock): + set_module_args({ + "id": 1212, + "monitor_tags": ["foo:bar"], + "scope": ["*"], + "monitor_id": 12345, + "downtime_message": "Message", + "start": 1111, + "end": 2222, + "timezone": "UTC", + "rrule": "rrule", + "api_key": "an_api_key", + "app_key": "an_app_key", + }) + + downtime = Downtime() + downtime.monitor_tags = ["foo:bar"] + downtime.scope = ["*"] + downtime.monitor_id = 12345 + downtime.message = "Message" + downtime.start = 1111 + downtime.end = 2222 + downtime.timezone = "UTC" + downtime.recurrence = DowntimeRecurrence( + rrule="rrule" + ) + + downtime_get = Downtime() + downtime_get.id = 1212 + downtime_get.disabled = False + downtime_get.monitor_tags = ["foo:bar"] + downtime_get.scope = ["*"] + downtime_get.monitor_id = 12345 + downtime_get.message = "Message" + downtime_get.start = 1111 + downtime_get.end = 2222 + downtime_get.timezone = "UTC" + downtime_get.recurrence = DowntimeRecurrence( + rrule="rrule" + ) + + update_downtime_mock = MagicMock(return_value=downtime_get) + get_downtime_mock = MagicMock(return_value=downtime_get) + downtimes_api_mock.return_value = MagicMock( + update_downtime=update_downtime_mock, get_downtime=get_downtime_mock + ) + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertFalse(result.exception.args[0]['changed']) + self.assertEqual(result.exception.args[0]['downtime']['id'], 1212) + update_downtime_mock.assert_called_once_with(1212, downtime) + get_downtime_mock.assert_called_once_with(1212) + + @patch("ansible_collections.community.general.plugins.modules.monitoring.datadog.datadog_downtime.DowntimesApi") + def test_delete_downtime(self, downtimes_api_mock): + set_module_args({ + "id": 1212, + "state": "absent", + "api_key": "an_api_key", + "app_key": "an_app_key", + }) + + cancel_downtime_mock = MagicMock() + get_downtime_mock = MagicMock(return_value=Downtime(id=1212)) + downtimes_api_mock.return_value = MagicMock( + get_downtime=get_downtime_mock, + cancel_downtime=cancel_downtime_mock + ) + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + cancel_downtime_mock.assert_called_once_with(1212) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_dconf.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_dconf.py new file mode 100644 index 000000000..e0ea8195a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_dconf.py @@ -0,0 +1,44 @@ +# 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 + +import pytest + +from ansible_collections.community.general.plugins.modules import dconf + +try: + from gi.repository.GLib import Variant +except ImportError: + Variant = None + +DconfPreference = dconf.DconfPreference + + +@pytest.mark.parametrize( + "v1,v2,expected,fallback_expected", + (("'foo'", "'foo'", True, True), + ('"foo"', "'foo'", True, False), + ("'foo'", '"foo"', True, False), + ("'foo'", '"bar"', False, False), + ("[1, 2, 3]", "[1, 2, 3]", True, True), + ("[1, 2, 3]", "[3, 2, 1]", False, False), + ('1234', '1234', True, True), + ('1234', '1235', False, False), + ('1.0', '1.0', True, True), + ('1.000', '1.0', True, False), + ('2.0', '4.0', False, False), + # GVariants with different types aren't equal! + ('1', '1.0', False, False), + # Explicit types + ('@as []', '[]', True, False), + )) +def test_gvariant_equality(mocker, v1, v2, expected, fallback_expected): + assert DconfPreference.variants_are_equal(v1, v2) is \ + (expected if Variant else fallback_expected) + mocker.patch.object(dconf, 'Variant', None) + mocker.patch.object(dconf, "GError", AttributeError) + assert DconfPreference.variants_are_equal(v1, v2) is fallback_expected diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_discord.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_discord.py new file mode 100644 index 000000000..83069d279 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_discord.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 +import pytest +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.plugins.modules import discord +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class TestDiscordModule(ModuleTestCase): + + def setUp(self): + super(TestDiscordModule, self).setUp() + self.module = discord + + def tearDown(self): + super(TestDiscordModule, self).tearDown() + + @pytest.fixture + def fetch_url_mock(self, mocker): + return mocker.patch('ansible.module_utils.notification.discord.fetch_url') + + def test_without_parameters(self): + """Failure if no parameters set""" + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + self.module.main() + + def test_without_content(self): + """Failure if content and embeds both are missing""" + set_module_args({ + 'webhook_id': 'xxx', + 'webhook_token': 'xxx' + }) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_successful_message(self): + """Test a basic message successfully.""" + set_module_args({ + 'webhook_id': 'xxx', + 'webhook_token': 'xxx', + 'content': 'test' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 204, 'msg': 'OK (0 bytes)'}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['content'] == "test" + + def test_message_with_username(self): + """Test a message with username set successfully.""" + set_module_args({ + 'webhook_id': 'xxx', + 'webhook_token': 'xxx', + 'content': 'test', + 'username': 'Ansible Bot' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 204, 'msg': 'OK (0 bytes)'}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['username'] == "Ansible Bot" + assert call_data['content'] == "test" + + def test_failed_message(self): + """Test failure because webhook id is wrong.""" + + set_module_args({ + 'webhook_id': 'wrong', + 'webhook_token': 'xxx', + 'content': 'test' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'HTTP Error 404: Not Found', 'body': '{"message": "Unknown Webhook", "code": 10015}'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_failed_message_without_body(self): + """Test failure with empty response body.""" + + set_module_args({ + 'webhook_id': 'wrong', + 'webhook_token': 'xxx', + 'content': 'test' + }) + + with patch.object(discord, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'HTTP Error 404: Not Found'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() 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 new file mode 100644 index 000000000..95a78818d --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py @@ -0,0 +1,64 @@ +# -*- 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 + +from ansible_collections.community.general.plugins.modules import dnsimple as dnsimple_module +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleFailJson, ModuleTestCase, set_module_args +from ansible_collections.community.general.tests.unit.compat.mock import patch +import pytest +import sys + +dnsimple = pytest.importorskip('dnsimple') +mandatory_py_version = pytest.mark.skipif( + sys.version_info < (3, 6), + reason='The dnsimple dependency requires python3.6 or higher' +) + +from dnsimple import DNSimpleException + + +class TestDNSimple(ModuleTestCase): + """Main class for testing dnsimple module.""" + + def setUp(self): + """Setup.""" + super(TestDNSimple, self).setUp() + self.module = dnsimple_module + + def tearDown(self): + """Teardown.""" + super(TestDNSimple, 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() + + @patch('dnsimple.service.Identity.whoami') + 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) + + @patch('dnsimple.service.Accounts.list_accounts') + @patch('dnsimple.service.Identity.whoami') + def test_user_token_multiple_accounts(self, mock_whoami, mock_accounts): + mock_accounts.return_value.data = [1, 2, 3] + mock_whoami.return_value.data.account = None + with self.assertRaises(DNSimpleException): + self.module.DNSimpleV2('fake', 'fake', True, self.module) + + @patch('dnsimple.service.Accounts.list_accounts') + @patch('dnsimple.service.Identity.whoami') + def test_user_token_single_account(self, mock_whoami, mock_accounts): + 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) 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 new file mode 100644 index 000000000..5806ec772 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py @@ -0,0 +1,111 @@ +# -*- 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 + +from ansible_collections.community.general.plugins.modules import dnsimple_info +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleFailJson, ModuleTestCase, set_module_args, AnsibleExitJson +from httmock import response +from httmock import with_httmock +from httmock import urlmatch + + +@urlmatch(netloc='(.)*dnsimple.com(.)*', + path='/v2/[0-9]*/zones/') +def zones_resp(url, request): + """return domains""" + headers = {'content-type': 'application/json'} + data_content = {"data": + [{"account_id": "1234", }, ], + "pagination": {"total_pages": 1}} + content = data_content + return response(200, content, headers, None, 5, request) + + +@urlmatch(netloc='(.)*dnsimple.com(.)*', + path='/v2/[0-9]*/zones/(.)*/records(.*)') +def records_resp(url, request): + """return record(s)""" + headers = {'content-type': 'application/json'} + data_content = {"data": + [{"content": "example", + "name": "example.com"}], + "pagination": {"total_pages": 1}} + content = data_content + return response(200, content, headers, None, 5, request) + + +class TestDNSimple_Info(ModuleTestCase): + """Main class for testing dnsimple module.""" + + def setUp(self): + + """Setup.""" + super(TestDNSimple_Info, self).setUp() + self.module = dnsimple_info + + def tearDown(self): + """Teardown.""" + super(TestDNSimple_Info, self).tearDown() + + def test_with_no_parameters(self): + """Failure must occurs when all parameters are missing""" + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + self.module.main() + + @with_httmock(zones_resp) + def test_only_key_and_account(self): + """key and account will pass, returns domains""" + account_id = "1234" + with self.assertRaises(AnsibleExitJson) as exc_info: + set_module_args({ + "api_key": "abcd1324", + "account_id": account_id + }) + self.module.main() + result = exc_info.exception.args[0] + # nothing should change + self.assertFalse(result['changed']) + # we should return at least one item with the matching account ID + assert result['dnsimple_domain_info'][0]["account_id"] == account_id + + @with_httmock(records_resp) + def test_only_name_without_record(self): + """name and no record should not fail, returns the record""" + name = "example.com" + with self.assertRaises(AnsibleExitJson) as exc_info: + set_module_args({ + "api_key": "abcd1324", + "name": "example.com", + "account_id": "1234" + }) + self.module.main() + result = exc_info.exception.args[0] + # nothing should change + self.assertFalse(result['changed']) + # we should return at least one item with mathing domain + assert result['dnsimple_records_info'][0]['name'] == name + + @with_httmock(records_resp) + def test_name_and_record(self): + """name and record should not fail, returns the record""" + record = "example" + with self.assertRaises(AnsibleExitJson) as exc_info: + set_module_args({ + "api_key": "abcd1324", + "account_id": "1234", + "name": "example.com", + "record": "example" + }) + self.module.main() + result = exc_info.exception.args[0] + # nothing should change + self.assertFalse(result['changed']) + # we should return at least one item and content should match + assert result['dnsimple_record_info'][0]['content'] == record 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 new file mode 100644 index 000000000..f01f15ef8 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py @@ -0,0 +1,116 @@ +# -*- 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 + +import json + +from ansible_collections.community.general.plugins.modules import gconftool2 + +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 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 new file mode 100644 index 000000000..352af6bb0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py @@ -0,0 +1,103 @@ +# -*- 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 + +import json + +from ansible_collections.community.general.plugins.modules import gconftool2_info + +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 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 new file mode 100644 index 000000000..92578e062 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py @@ -0,0 +1,143 @@ +# Copyright (c) 2018 Antoine Catton +# MIT License (see LICENSES/MIT.txt or https://opensource.org/licenses/MIT) +# SPDX-License-Identifier: MIT +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import copy + +import pytest + +from ansible_collections.community.general.plugins.modules import gem +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +def get_command(run_command): + """Generate the command line string from the patched run_command""" + args = run_command.call_args[0] + command = args[0] + return ' '.join(command) + + +class TestGem(ModuleTestCase): + def setUp(self): + super(TestGem, self).setUp() + self.rubygems_path = ['/usr/bin/gem'] + self.mocker.patch( + 'ansible_collections.community.general.plugins.modules.gem.get_rubygems_path', + lambda module: copy.deepcopy(self.rubygems_path), + ) + + @pytest.fixture(autouse=True) + def _mocker(self, mocker): + self.mocker = mocker + + def patch_installed_versions(self, versions): + """Mocks the versions of the installed package""" + + target = 'ansible_collections.community.general.plugins.modules.gem.get_installed_versions' + + def new(module, remote=False): + return versions + + return self.mocker.patch(target, new) + + def patch_rubygems_version(self, version=None): + target = 'ansible_collections.community.general.plugins.modules.gem.get_rubygems_version' + + def new(module): + return version + + return self.mocker.patch(target, new) + + def patch_run_command(self): + target = 'ansible.module_utils.basic.AnsibleModule.run_command' + return self.mocker.patch(target) + + def test_fails_when_user_install_and_install_dir_are_combined(self): + set_module_args({ + 'name': 'dummy', + 'user_install': True, + 'install_dir': '/opt/dummy', + }) + + with pytest.raises(AnsibleFailJson) as exc: + gem.main() + + result = exc.value.args[0] + assert result['failed'] + 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 + # 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 + # invoked with '--install-dir'. + + set_module_args({ + 'name': 'dummy', + 'user_install': False, + 'install_dir': '/opt/dummy', + }) + + self.patch_rubygems_version() + self.patch_installed_versions([]) + run_command = self.patch_run_command() + + with pytest.raises(AnsibleExitJson) as exc: + gem.main() + + result = exc.value.args[0] + assert result['changed'] + assert run_command.called + + assert '--install-dir /opt/dummy' in get_command(run_command) + + def test_passes_install_dir_and_gem_home_when_uninstall_gem(self): + # XXX: This test is also extremely fragile because of mocking. + # If this breaks, the only that matters is to check whether '--install-dir' is + # in the run command, and that GEM_HOME is passed to the command. + set_module_args({ + 'name': 'dummy', + 'user_install': False, + 'install_dir': '/opt/dummy', + 'state': 'absent', + }) + + self.patch_rubygems_version() + self.patch_installed_versions(['1.0.0']) + + run_command = self.patch_run_command() + + with pytest.raises(AnsibleExitJson) as exc: + gem.main() + + result = exc.value.args[0] + + assert result['changed'] + assert run_command.called + + assert '--install-dir /opt/dummy' in get_command(run_command) + + update_environ = run_command.call_args[1].get('environ_update', {}) + assert update_environ.get('GEM_HOME') == '/opt/dummy' + + def test_passes_add_force_option(self): + set_module_args({ + 'name': 'dummy', + 'force': True, + }) + + self.patch_rubygems_version() + self.patch_installed_versions([]) + run_command = self.patch_run_command() + + with pytest.raises(AnsibleExitJson) as exc: + gem.main() + + result = exc.value.args[0] + assert result['changed'] + assert run_command.called + + assert '--force' in get_command(run_command) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_github_repo.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_github_repo.py new file mode 100644 index 000000000..10227aadf --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_github_repo.py @@ -0,0 +1,330 @@ +# 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 re +import json +import sys +from httmock import with_httmock, urlmatch, response +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.plugins.modules import github_repo + +GITHUB_MINIMUM_PYTHON_VERSION = (2, 7) + + +@urlmatch(netloc=r'.*') +def debug_mock(url, request): + print(request.original.__dict__) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/orgs/.*', method="get") +def get_orgs_mock(url, request): + match = re.search(r"api\.github\.com(:[0-9]+)?/orgs/(?P[^/]+)", request.url) + org = match.group("org") + + # https://docs.github.com/en/rest/reference/orgs#get-an-organization + headers = {'content-type': 'application/json'} + content = { + "login": org, + "url": "https://api.github.com/orgs/{0}".format(org) + } + content = json.dumps(content).encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/user', method="get") +def get_user_mock(url, request): + # https://docs.github.com/en/rest/reference/users#get-the-authenticated-user + headers = {'content-type': 'application/json'} + content = { + "login": "octocat", + "url": "https://api.github.com/users/octocat" + } + content = json.dumps(content).encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="get") +def get_repo_notfound_mock(url, request): + return response(404, "{\"message\": \"Not Found\"}", "", "Not Found", 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="get") +def get_repo_mock(url, request): + match = re.search( + r"api\.github\.com(:[0-9]+)?/repos/(?P[^/]+)/(?P[^/]+)", request.url) + org = match.group("org") + repo = match.group("repo") + + # https://docs.github.com/en/rest/reference/repos#get-a-repository + headers = {'content-type': 'application/json'} + content = { + "name": repo, + "full_name": "{0}/{1}".format(org, repo), + "url": "https://api.github.com/repos/{0}/{1}".format(org, repo), + "private": False, + "description": "This your first repo!", + "default_branch": "master", + "allow_rebase_merge": True + } + content = json.dumps(content).encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="get") +def get_private_repo_mock(url, request): + match = re.search( + r"api\.github\.com(:[0-9]+)?/repos/(?P[^/]+)/(?P[^/]+)", request.url) + org = match.group("org") + repo = match.group("repo") + + # https://docs.github.com/en/rest/reference/repos#get-a-repository + headers = {'content-type': 'application/json'} + content = { + "name": repo, + "full_name": "{0}/{1}".format(org, repo), + "url": "https://api.github.com/repos/{0}/{1}".format(org, repo), + "private": True, + "description": "This your first repo!", + "default_branch": "master", + "allow_rebase_merge": True + } + content = json.dumps(content).encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/orgs/.*/repos', method="post") +def create_new_org_repo_mock(url, request): + match = re.search( + r"api\.github\.com(:[0-9]+)?/orgs/(?P[^/]+)/repos", request.url) + org = match.group("org") + repo = json.loads(request.body) + + headers = {'content-type': 'application/json'} + # https://docs.github.com/en/rest/reference/repos#create-an-organization-repository + content = { + "name": repo['name'], + "full_name": "{0}/{1}".format(org, repo['name']), + "private": repo.get('private', False), + "description": repo.get('description') + } + content = json.dumps(content).encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/user/repos', method="post") +def create_new_user_repo_mock(url, request): + repo = json.loads(request.body) + + headers = {'content-type': 'application/json'} + # https://docs.github.com/en/rest/reference/repos#create-a-repository-for-the-authenticated-user + content = { + "name": repo['name'], + "full_name": "{0}/{1}".format("octocat", repo['name']), + "private": repo.get('private', False), + "description": repo.get('description') + } + content = json.dumps(content).encode("utf-8") + return response(201, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="patch") +def patch_repo_mock(url, request): + match = re.search( + r"api\.github\.com(:[0-9]+)?/repos/(?P[^/]+)/(?P[^/]+)", request.url) + org = match.group("org") + repo = match.group("repo") + + body = json.loads(request.body) + headers = {'content-type': 'application/json'} + # https://docs.github.com/en/rest/reference/repos#update-a-repository + content = { + "name": repo, + "full_name": "{0}/{1}".format(org, repo), + "url": "https://api.github.com/repos/{0}/{1}".format(org, repo), + "private": body.get('private', False), + "description": body.get('description'), + "default_branch": "master", + "allow_rebase_merge": True + } + content = json.dumps(content).encode("utf-8") + return response(200, content, headers, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="delete") +def delete_repo_mock(url, request): + # https://docs.github.com/en/rest/reference/repos#delete-a-repository + return response(204, None, None, None, 5, request) + + +@urlmatch(netloc=r'api\.github\.com(:[0-9]+)?$', path=r'/repos/.*/.*', method="delete") +def delete_repo_notfound_mock(url, request): + # https://docs.github.com/en/rest/reference/repos#delete-a-repository + return response(404, "{\"message\": \"Not Found\"}", "", "Not Found", 5, request) + + +class TestGithubRepo(unittest.TestCase): + def setUp(self): + if sys.version_info < GITHUB_MINIMUM_PYTHON_VERSION: + self.skipTest("Python %s+ is needed for PyGithub" % + ",".join(map(str, GITHUB_MINIMUM_PYTHON_VERSION))) + + @with_httmock(get_orgs_mock) + @with_httmock(get_repo_notfound_mock) + @with_httmock(create_new_org_repo_mock) + def test_create_new_org_repo(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": "MyOrganization", + "name": "myrepo", + "description": "Just for fun", + "private": False, + "state": "present", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + + self.assertEqual(result['changed'], True) + self.assertEqual(result['repo']['private'], False) + self.assertEqual(result['repo']['description'], 'Just for fun') + + @with_httmock(get_orgs_mock) + @with_httmock(get_repo_notfound_mock) + @with_httmock(create_new_org_repo_mock) + def test_create_new_org_repo_incomplete(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": "MyOrganization", + "name": "myrepo", + "description": None, + "private": None, + "state": "present", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + + self.assertEqual(result['changed'], True) + self.assertEqual(result['repo']['private'], False) + self.assertEqual(result['repo']['description'], None) + + @with_httmock(get_user_mock) + @with_httmock(get_repo_notfound_mock) + @with_httmock(create_new_user_repo_mock) + def test_create_new_user_repo(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": None, + "name": "myrepo", + "description": "Just for fun", + "private": True, + "state": "present", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + self.assertEqual(result['changed'], True) + self.assertEqual(result['repo']['private'], True) + + @with_httmock(get_orgs_mock) + @with_httmock(get_repo_mock) + @with_httmock(patch_repo_mock) + def test_patch_existing_org_repo(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": "MyOrganization", + "name": "myrepo", + "description": "Just for fun", + "private": True, + "state": "present", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + self.assertEqual(result['changed'], True) + self.assertEqual(result['repo']['private'], True) + + @with_httmock(get_orgs_mock) + @with_httmock(get_private_repo_mock) + def test_idempotency_existing_org_private_repo(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": "MyOrganization", + "name": "myrepo", + "description": None, + "private": None, + "state": "present", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + self.assertEqual(result['changed'], False) + self.assertEqual(result['repo']['private'], True) + self.assertEqual(result['repo']['description'], 'This your first repo!') + + @with_httmock(get_orgs_mock) + @with_httmock(get_repo_mock) + @with_httmock(delete_repo_mock) + def test_delete_org_repo(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": "MyOrganization", + "name": "myrepo", + "description": "Just for fun", + "private": False, + "state": "absent", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + self.assertEqual(result['changed'], True) + + @with_httmock(get_user_mock) + @with_httmock(get_repo_mock) + @with_httmock(delete_repo_mock) + def test_delete_user_repo(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": None, + "name": "myrepo", + "description": "Just for fun", + "private": False, + "state": "absent", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + self.assertEqual(result['changed'], True) + + @with_httmock(get_orgs_mock) + @with_httmock(get_repo_notfound_mock) + @with_httmock(delete_repo_notfound_mock) + def test_delete_org_repo_notfound(self): + result = github_repo.run_module({ + 'username': None, + 'password': None, + "access_token": "mytoken", + "organization": "MyOrganization", + "name": "myrepo", + "description": "Just for fun", + "private": True, + "state": "absent", + "api_url": "https://api.github.com", + "force_defaults": False, + }) + self.assertEqual(result['changed'], False) + + +if __name__ == "__main__": + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_deploy_key.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_deploy_key.py new file mode 100644 index 000000000..3e4dc5856 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_deploy_key.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 + +from ansible_collections.community.general.plugins.modules.gitlab_deploy_key import GitLabDeployKey + + +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, + python_version_match_requirement, + resp_get_project, resp_find_project_deploy_key, + resp_create_project_deploy_key, resp_delete_project_deploy_key) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import ProjectKey +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_get_project = _dummy + resp_find_project_deploy_key = _dummy + resp_create_project_deploy_key = _dummy + resp_delete_project_deploy_key = _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 TestGitlabDeployKey(GitlabModuleTestCase): + def setUp(self): + super(TestGitlabDeployKey, self).setUp() + + self.moduleUtil = GitLabDeployKey(module=self.mock_module, gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_get_project) + @with_httmock(resp_find_project_deploy_key) + def test_deploy_key_exist(self): + project = self.gitlab_instance.projects.get(1) + + rvalue = self.moduleUtil.exists_deploy_key(project, "Public key") + + self.assertEqual(rvalue, True) + + rvalue = self.moduleUtil.exists_deploy_key(project, "Private key") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_get_project) + @with_httmock(resp_create_project_deploy_key) + def test_create_deploy_key(self): + project = self.gitlab_instance.projects.get(1) + + deploy_key = self.moduleUtil.create_deploy_key(project, {"title": "Public key", + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM" + "4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxc" + "KDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfD" + "zpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0="}) + + self.assertEqual(type(deploy_key), ProjectKey) + self.assertEqual(deploy_key.title, "Public key") + + @with_httmock(resp_get_project) + @with_httmock(resp_find_project_deploy_key) + @with_httmock(resp_create_project_deploy_key) + def test_update_deploy_key(self): + project = self.gitlab_instance.projects.get(1) + deploy_key = self.moduleUtil.find_deploy_key(project, "Public key") + + changed, newDeploy_key = self.moduleUtil.update_deploy_key(deploy_key, {"title": "Private key"}) + + self.assertEqual(changed, True) + self.assertEqual(type(newDeploy_key), ProjectKey) + self.assertEqual(newDeploy_key.title, "Private key") + + changed, newDeploy_key = self.moduleUtil.update_deploy_key(deploy_key, {"title": "Private key"}) + + self.assertEqual(changed, False) + self.assertEqual(newDeploy_key.title, "Private key") + + @with_httmock(resp_get_project) + @with_httmock(resp_find_project_deploy_key) + @with_httmock(resp_delete_project_deploy_key) + def test_delete_deploy_key(self): + project = self.gitlab_instance.projects.get(1) + + self.moduleUtil.exists_deploy_key(project, "Public key") + + rvalue = self.moduleUtil.delete_deploy_key() + + self.assertEqual(rvalue, None) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group.py new file mode 100644 index 000000000..230c47030 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 + +from ansible_collections.community.general.plugins.modules.gitlab_group import GitLabGroup + + +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, + python_version_match_requirement, + resp_get_group, resp_get_missing_group, resp_create_group, + resp_create_subgroup, resp_delete_group, resp_find_group_project) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import Group +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_get_group = _dummy + resp_get_missing_group = _dummy + resp_create_group = _dummy + resp_create_subgroup = _dummy + resp_delete_group = _dummy + resp_find_group_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 TestGitlabGroup(GitlabModuleTestCase): + def setUp(self): + super(TestGitlabGroup, self).setUp() + + self.moduleUtil = GitLabGroup(module=self.mock_module, gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_get_group) + def test_exist_group(self): + rvalue = self.moduleUtil.exists_group(1) + + self.assertEqual(rvalue, True) + + @with_httmock(resp_get_missing_group) + def test_exist_group(self): + rvalue = self.moduleUtil.exists_group(1) + + self.assertEqual(rvalue, False) + + @with_httmock(resp_create_group) + def test_create_group(self): + group = self.moduleUtil.create_group({'name': "Foobar Group", + 'path': "foo-bar", + 'description': "An interesting group", + 'project_creation_level': "developer", + 'subgroup_creation_level': "maintainer", + 'require_two_factor_authentication': True, + }) + + self.assertEqual(type(group), Group) + self.assertEqual(group.name, "Foobar Group") + self.assertEqual(group.path, "foo-bar") + self.assertEqual(group.description, "An interesting group") + self.assertEqual(group.project_creation_level, "developer") + self.assertEqual(group.subgroup_creation_level, "maintainer") + self.assertEqual(group.require_two_factor_authentication, True) + self.assertEqual(group.id, 1) + + @with_httmock(resp_create_subgroup) + def test_create_subgroup(self): + group = self.moduleUtil.create_group({'name': "BarFoo Group", + 'path': "bar-foo", + 'parent_id': 1, + 'project_creation_level': "noone", + 'require_two_factor_authentication': True, + }) + + self.assertEqual(type(group), Group) + self.assertEqual(group.name, "BarFoo Group") + self.assertEqual(group.full_path, "foo-bar/bar-foo") + self.assertEqual(group.project_creation_level, "noone") + self.assertEqual(group.require_two_factor_authentication, True) + self.assertEqual(group.id, 2) + self.assertEqual(group.parent_id, 1) + + @with_httmock(resp_get_group) + def test_update_group(self): + group = self.gitlab_instance.groups.get(1) + changed, newGroup = self.moduleUtil.update_group(group, {'name': "BarFoo Group", + 'visibility': "private", + 'project_creation_level': "maintainer", + 'require_two_factor_authentication': True, + }) + + self.assertEqual(changed, True) + self.assertEqual(newGroup.name, "BarFoo Group") + self.assertEqual(newGroup.visibility, "private") + self.assertEqual(newGroup.project_creation_level, "maintainer") + self.assertEqual(newGroup.require_two_factor_authentication, True) + + changed, newGroup = self.moduleUtil.update_group(group, {'name': "BarFoo Group"}) + + self.assertEqual(changed, False) + + @with_httmock(resp_get_group) + @with_httmock(resp_find_group_project) + @with_httmock(resp_delete_group) + def test_delete_group(self): + self.moduleUtil.exists_group(1) + + print(self.moduleUtil.group_object.projects) + + rvalue = self.moduleUtil.delete_group() + + self.assertEqual(rvalue, None) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_hook.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_hook.py new file mode 100644 index 000000000..b9c72e19e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_hook.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 + +from ansible_collections.community.general.plugins.modules.gitlab_hook import GitLabHook + + +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, + python_version_match_requirement, + resp_get_project, resp_find_project_hook, + resp_create_project_hook, resp_delete_project_hook) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import ProjectHook +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_get_project = _dummy + resp_find_project_hook = _dummy + resp_create_project_hook = _dummy + resp_delete_project_hook = _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 TestGitlabHook(GitlabModuleTestCase): + def setUp(self): + super(TestGitlabHook, self).setUp() + + self.moduleUtil = GitLabHook(module=self.mock_module, gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_get_project) + @with_httmock(resp_find_project_hook) + def test_hook_exist(self): + project = self.gitlab_instance.projects.get(1) + + rvalue = self.moduleUtil.exists_hook(project, "http://example.com/hook") + + self.assertEqual(rvalue, True) + + rvalue = self.moduleUtil.exists_hook(project, "http://gitlab.com/hook") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_get_project) + @with_httmock(resp_create_project_hook) + def test_create_hook(self): + project = self.gitlab_instance.projects.get(1) + + hook = self.moduleUtil.create_hook(project, {"url": "http://example.com/hook"}) + + self.assertEqual(type(hook), ProjectHook) + self.assertEqual(hook.url, "http://example.com/hook") + + @with_httmock(resp_get_project) + @with_httmock(resp_find_project_hook) + def test_update_hook(self): + project = self.gitlab_instance.projects.get(1) + hook = self.moduleUtil.find_hook(project, "http://example.com/hook") + + changed, newHook = self.moduleUtil.update_hook(hook, {"url": "http://gitlab.com/hook"}) + + self.assertEqual(changed, True) + self.assertEqual(type(newHook), ProjectHook) + self.assertEqual(newHook.url, "http://gitlab.com/hook") + + changed, newHook = self.moduleUtil.update_hook(hook, {"url": "http://gitlab.com/hook"}) + + self.assertEqual(changed, False) + self.assertEqual(newHook.url, "http://gitlab.com/hook") + + @with_httmock(resp_get_project) + @with_httmock(resp_find_project_hook) + @with_httmock(resp_delete_project_hook) + def test_delete_hook(self): + project = self.gitlab_instance.projects.get(1) + + self.moduleUtil.exists_hook(project, "http://example.com/hook") + + rvalue = self.moduleUtil.delete_hook() + + self.assertEqual(rvalue, None) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project.py new file mode 100644 index 000000000..397f79bcb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 + +from ansible_collections.community.general.plugins.modules.gitlab_project import GitLabProject + + +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, + python_version_match_requirement, + resp_get_group, resp_get_project_by_name, resp_create_project, + resp_get_project, resp_delete_project, resp_get_user) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import Project +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_get_group = _dummy + resp_get_project_by_name = _dummy + resp_create_project = _dummy + resp_get_project = _dummy + resp_delete_project = _dummy + resp_get_user = _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 TestGitlabProject(GitlabModuleTestCase): + @with_httmock(resp_get_user) + def setUp(self): + super(TestGitlabProject, self).setUp() + + self.gitlab_instance.user = self.gitlab_instance.users.get(1) + self.moduleUtil = GitLabProject(module=self.mock_module, gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_get_group) + @with_httmock(resp_get_project_by_name) + def test_project_exist(self): + group = self.gitlab_instance.groups.get(1) + + rvalue = self.moduleUtil.exists_project(group, "diaspora-client") + + self.assertEqual(rvalue, True) + + rvalue = self.moduleUtil.exists_project(group, "missing-project") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_get_group) + @with_httmock(resp_create_project) + def test_create_project(self): + group = self.gitlab_instance.groups.get(1) + project = self.moduleUtil.create_project(group, {"name": "Diaspora Client", "path": "diaspora-client", "namespace_id": group.id}) + + self.assertEqual(type(project), Project) + self.assertEqual(project.name, "Diaspora Client") + + @with_httmock(resp_get_project) + def test_update_project(self): + project = self.gitlab_instance.projects.get(1) + + changed, newProject = self.moduleUtil.update_project(project, {"name": "New Name"}) + + self.assertEqual(changed, True) + self.assertEqual(type(newProject), Project) + self.assertEqual(newProject.name, "New Name") + + changed, newProject = self.moduleUtil.update_project(project, {"name": "New Name"}) + + self.assertEqual(changed, False) + self.assertEqual(newProject.name, "New Name") + + @with_httmock(resp_get_project) + def test_update_project_merge_method(self): + project = self.gitlab_instance.projects.get(1) + + # merge_method should be 'merge' by default + self.assertEqual(project.merge_method, "merge") + + changed, newProject = self.moduleUtil.update_project(project, {"name": "New Name", "merge_method": "rebase_merge"}) + + self.assertEqual(changed, True) + self.assertEqual(type(newProject), Project) + self.assertEqual(newProject.name, "New Name") + self.assertEqual(newProject.merge_method, "rebase_merge") + + changed, newProject = self.moduleUtil.update_project(project, {"name": "New Name", "merge_method": "rebase_merge"}) + + self.assertEqual(changed, False) + self.assertEqual(newProject.name, "New Name") + self.assertEqual(newProject.merge_method, "rebase_merge") + + @with_httmock(resp_get_group) + @with_httmock(resp_get_project_by_name) + @with_httmock(resp_delete_project) + def test_delete_project(self): + group = self.gitlab_instance.groups.get(1) + + self.moduleUtil.exists_project(group, "diaspora-client") + + rvalue = self.moduleUtil.delete_project() + + self.assertEqual(rvalue, None) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_protected_branch.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_protected_branch.py new file mode 100644 index 000000000..1162d8a35 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_protected_branch.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 + +from ansible_collections.community.general.plugins.module_utils.version import LooseVersion + +from ansible_collections.community.general.plugins.modules.gitlab_protected_branch import GitlabProtectedBranch + + +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, + python_version_match_requirement, python_gitlab_module_version, + python_gitlab_version_match_requirement, + resp_get_protected_branch, resp_get_project_by_name, + resp_get_protected_branch_not_exist, + resp_delete_protected_branch, resp_get_user) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import Project # noqa: F401, pylint: disable=unused-import + gitlab_req_version = python_gitlab_version_match_requirement() + gitlab_module_version = python_gitlab_module_version() + if LooseVersion(gitlab_module_version) < LooseVersion(gitlab_req_version): + pytestmark.append(pytest.mark.skip("Could not load gitlab module required for testing (Wrong version)")) +except ImportError: + pytestmark.append(pytest.mark.skip("Could not load gitlab module required for testing")) + +# 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 TestGitlabProtectedBranch(GitlabModuleTestCase): + @with_httmock(resp_get_project_by_name) + @with_httmock(resp_get_user) + def setUp(self): + super(TestGitlabProtectedBranch, self).setUp() + + self.gitlab_instance.user = self.gitlab_instance.users.get(1) + self.moduleUtil = GitlabProtectedBranch(module=self.mock_module, project="foo-bar/diaspora-client", gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_get_protected_branch) + def test_protected_branch_exist(self): + rvalue = self.moduleUtil.protected_branch_exist(name="master") + self.assertEqual(rvalue.name, "master") + + @with_httmock(resp_get_protected_branch_not_exist) + def test_protected_branch_exist_not_exist(self): + rvalue = self.moduleUtil.protected_branch_exist(name="master") + self.assertEqual(rvalue, False) + + @with_httmock(resp_get_protected_branch) + def test_compare_protected_branch(self): + rvalue = self.moduleUtil.compare_protected_branch(name="master", merge_access_levels="maintainer", push_access_level="maintainer") + self.assertEqual(rvalue, True) + + @with_httmock(resp_get_protected_branch) + def test_compare_protected_branch_different_settings(self): + rvalue = self.moduleUtil.compare_protected_branch(name="master", merge_access_levels="developer", push_access_level="maintainer") + self.assertEqual(rvalue, False) + + @with_httmock(resp_get_protected_branch) + @with_httmock(resp_delete_protected_branch) + def test_delete_protected_branch(self): + rvalue = self.moduleUtil.delete_protected_branch(name="master") + self.assertEqual(rvalue, None) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_runner.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_runner.py new file mode 100644 index 000000000..987659e9c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_runner.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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) + +import gitlab +__metaclass__ = type + +import pytest + +from ansible_collections.community.general.plugins.modules.gitlab_runner import GitLabRunner + + +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 (FakeAnsibleModule, + GitlabModuleTestCase, + python_version_match_requirement, + resp_find_runners_all, resp_find_runners_list, + resp_find_project_runners, resp_find_group_runners, + resp_get_runner, + resp_create_runner, resp_delete_runner, + resp_get_project_by_name, resp_get_group_by_name) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import Runner +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_find_runners_list = _dummy + resp_get_runner = _dummy + resp_create_runner = _dummy + resp_delete_runner = _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 TestGitlabRunner(GitlabModuleTestCase): + def setUp(self): + super(TestGitlabRunner, self).setUp() + + self.module_util_all = GitLabRunner(module=FakeAnsibleModule({"owned": False}), gitlab_instance=self.gitlab_instance) + self.module_util_owned = GitLabRunner(module=FakeAnsibleModule({"owned": True}), gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_find_runners_all) + @with_httmock(resp_get_runner) + def test_runner_exist_all(self): + rvalue = self.module_util_all.exists_runner("test-1-20150125") + + self.assertEqual(rvalue, True) + + rvalue = self.module_util_all.exists_runner("test-3-00000000") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_find_runners_list) + @with_httmock(resp_get_runner) + def test_runner_exist_owned(self): + rvalue = self.module_util_owned.exists_runner("test-1-20201214") + + self.assertEqual(rvalue, True) + + rvalue = self.module_util_owned.exists_runner("test-3-00000000") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_find_project_runners) + @with_httmock(resp_get_runner) + @with_httmock(resp_get_project_by_name) + def test_project_runner_exist(self): + gitlab_project = self.gitlab_instance.projects.get('foo-bar/diaspora-client') + module_util = GitLabRunner(module=FakeAnsibleModule(), gitlab_instance=self.gitlab_instance, project=gitlab_project) + + rvalue = module_util.exists_runner("test-1-20220210") + + self.assertEqual(rvalue, True) + + rvalue = module_util.exists_runner("test-3-00000000") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_find_group_runners) + @with_httmock(resp_get_group_by_name) + @with_httmock(resp_get_runner) + @pytest.mark.skipif(gitlab.__version__ < "2.3.0", reason="require python-gitlab >= 2.3.0") + def test_group_runner_exist(self): + gitlab_group = self.gitlab_instance.groups.get('foo-bar') + module_util = GitLabRunner(module=FakeAnsibleModule(), gitlab_instance=self.gitlab_instance, group=gitlab_group) + + rvalue = module_util.exists_runner("test-3-20220210") + + self.assertEqual(rvalue, True) + + rvalue = module_util.exists_runner("test-3-00000000") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_create_runner) + def test_create_runner(self): + runner = self.module_util_all.create_runner({"token": "token", "description": "test-1-20150125"}) + + self.assertEqual(type(runner), Runner) + self.assertEqual(runner.description, "test-1-20150125") + + @with_httmock(resp_find_runners_all) + @with_httmock(resp_get_runner) + def test_update_runner(self): + runner = self.module_util_all.find_runner("test-1-20150125") + + changed, newRunner = self.module_util_all.update_runner(runner, {"description": "Runner description"}) + + self.assertEqual(changed, True) + self.assertEqual(type(newRunner), Runner) + self.assertEqual(newRunner.description, "Runner description") + + changed, newRunner = self.module_util_all.update_runner(runner, {"description": "Runner description"}) + + self.assertEqual(changed, False) + self.assertEqual(newRunner.description, "Runner description") + + @with_httmock(resp_find_runners_all) + @with_httmock(resp_get_runner) + @with_httmock(resp_delete_runner) + def test_delete_runner(self): + self.module_util_all.exists_runner("test-1-20150125") + + rvalue = self.module_util_all.delete_runner() + + self.assertEqual(rvalue, None) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_user.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_user.py new file mode 100644 index 000000000..6dd2fce1d --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_user.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2019, Guillaume Martinez (lunik@tiwabbit.fr) +# 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 + +from ansible_collections.community.general.plugins.modules.gitlab_user import GitLabUser + + +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, + python_version_match_requirement, + resp_find_user, resp_get_user, resp_get_user_keys, + resp_create_user_keys, resp_create_user, resp_delete_user, + resp_get_member, resp_get_group, resp_add_member, + resp_update_member, resp_get_member) + + # GitLab module requirements + if python_version_match_requirement(): + from gitlab.v4.objects import User +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_find_user = _dummy + resp_get_user = _dummy + resp_get_user_keys = _dummy + resp_create_user_keys = _dummy + resp_create_user = _dummy + resp_delete_user = _dummy + resp_get_member = _dummy + resp_get_group = _dummy + resp_add_member = _dummy + resp_update_member = _dummy + resp_get_member = _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 TestGitlabUser(GitlabModuleTestCase): + def setUp(self): + super(TestGitlabUser, self).setUp() + + self.moduleUtil = GitLabUser(module=self.mock_module, gitlab_instance=self.gitlab_instance) + + @with_httmock(resp_find_user) + def test_exist_user(self): + rvalue = self.moduleUtil.exists_user("john_smith") + + self.assertEqual(rvalue, True) + + rvalue = self.moduleUtil.exists_user("paul_smith") + + self.assertEqual(rvalue, False) + + @with_httmock(resp_find_user) + def test_find_user(self): + user = self.moduleUtil.find_user("john_smith") + + self.assertEqual(type(user), User) + self.assertEqual(user.name, "John Smith") + self.assertEqual(user.id, 1) + + @with_httmock(resp_create_user) + def test_create_user(self): + user = self.moduleUtil.create_user({'email': 'john@example.com', 'password': 's3cur3s3cr3T', + 'username': 'john_smith', 'name': 'John Smith'}) + self.assertEqual(type(user), User) + self.assertEqual(user.name, "John Smith") + self.assertEqual(user.id, 1) + + @with_httmock(resp_get_user) + def test_update_user(self): + user = self.gitlab_instance.users.get(1) + + changed, newUser = self.moduleUtil.update_user( + user, + {'name': {'value': "Jack Smith"}, "is_admin": {'value': "true", 'setter': 'admin'}}, {} + ) + + self.assertEqual(changed, True) + self.assertEqual(newUser.name, "Jack Smith") + self.assertEqual(newUser.admin, "true") + + changed, newUser = self.moduleUtil.update_user(user, {'name': {'value': "Jack Smith"}}, {}) + + self.assertEqual(changed, False) + + changed, newUser = self.moduleUtil.update_user( + user, + {}, { + 'skip_reconfirmation': {'value': True}, + 'password': {'value': 'super_secret-super_secret'}, + } + ) + + # note: uncheckable parameters dont set changed state + self.assertEqual(changed, False) + self.assertEqual(newUser.skip_reconfirmation, True) + self.assertEqual(newUser.password, 'super_secret-super_secret') + + @with_httmock(resp_find_user) + @with_httmock(resp_delete_user) + def test_delete_user(self): + self.moduleUtil.exists_user("john_smith") + rvalue = self.moduleUtil.delete_user() + + self.assertEqual(rvalue, None) + + @with_httmock(resp_get_user) + @with_httmock(resp_get_user_keys) + def test_sshkey_exist(self): + user = self.gitlab_instance.users.get(1) + + exist = self.moduleUtil.ssh_key_exists(user, "Public key") + self.assertEqual(exist, True) + + notExist = self.moduleUtil.ssh_key_exists(user, "Private key") + self.assertEqual(notExist, False) + + @with_httmock(resp_get_user) + @with_httmock(resp_create_user_keys) + @with_httmock(resp_get_user_keys) + def test_create_sshkey(self): + user = self.gitlab_instance.users.get(1) + + rvalue = self.moduleUtil.add_ssh_key_to_user(user, { + 'name': "Public key", + 'file': "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJe" + "jgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4" + "soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", + 'expires_at': ""}) + self.assertEqual(rvalue, False) + + rvalue = self.moduleUtil.add_ssh_key_to_user(user, { + 'name': "Private key", + 'file': "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDA1YotVDm2mAyk2tPt4E7AHm01sS6JZmcU" + "dRuSuA5zszUJzYPPUSRAX3BCgTqLqYx//UuVncK7YqLVSbbwjKR2Ez5lISgCnVfLVEXzwhv+" + "xawxKWmI7hJ5S0tOv6MJ+IxyTa4xcKwJTwB86z22n9fVOQeJTR2dSOH1WJrf0PvRk+KVNY2j" + "TiGHTi9AIjLnyD/jWRpOgtdfkLRc8EzAWrWlgNmH2WOKBw6za0az6XoG75obUdFVdW3qcD0x" + "c809OHLi7FDf+E7U4wiZJCFuUizMeXyuK/SkaE1aee4Qp5R4dxTR4TP9M1XAYkf+kF0W9srZ+mhF069XD/zhUPJsvwEF", + 'expires_at': "2027-01-01"}) + self.assertEqual(rvalue, True) + + @with_httmock(resp_get_group) + @with_httmock(resp_get_member) + def test_find_member(self): + group = self.gitlab_instance.groups.get(1) + + user = self.moduleUtil.find_member(group, 1) + self.assertEqual(user.username, "raymond_smith") + + @with_httmock(resp_get_user) + @with_httmock(resp_get_group) + @with_httmock(resp_get_group) + @with_httmock(resp_get_member) + @with_httmock(resp_add_member) + @with_httmock(resp_update_member) + def test_assign_user_to_group(self): + group = self.gitlab_instance.groups.get(1) + user = self.gitlab_instance.users.get(1) + + rvalue = self.moduleUtil.assign_user_to_group(user, group.id, "developer") + self.assertEqual(rvalue, False) + + rvalue = self.moduleUtil.assign_user_to_group(user, group.id, "guest") + 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 new file mode 100644 index 000000000..db06e4cef --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_hana_query.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Rainer Leber (@rainerleber) +# 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_homebrew.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py new file mode 100644 index 000000000..f849b433d --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py @@ -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 + +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.plugins.modules.homebrew import Homebrew + + +class TestHomebrewModule(unittest.TestCase): + + def setUp(self): + self.brew_app_names = [ + 'git-ssh', + 'awscli@1', + 'bash' + ] + + def test_valid_package_names(self): + for name in self.brew_app_names: + self.assertTrue(Homebrew.valid_package(name)) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew_cask.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew_cask.py new file mode 100644 index 000000000..6fcc06d97 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew_cask.py @@ -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 + +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.plugins.modules.homebrew_cask import HomebrewCask + + +class TestHomebrewCaskModule(unittest.TestCase): + + def setUp(self): + self.brew_cask_names = [ + 'visual-studio-code', + 'firefox' + ] + + def test_valid_cask_names(self): + for name in self.brew_cask_names: + self.assertTrue(HomebrewCask.valid_cask(name)) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_icinga2_feature.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_icinga2_feature.py new file mode 100644 index 000000000..23c94fad5 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_icinga2_feature.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2018, Ansible Project +# Copyright (c) 2018, Abhijeet Kasurde +# +# 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 icinga2_feature +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 icinga2 binary.""" + return "/bin/icinga2" + + +class TestIcinga2Feature(ModuleTestCase): + """Main class for testing icinga2_feature module.""" + + def setUp(self): + """Setup.""" + super(TestIcinga2Feature, self).setUp() + self.module = icinga2_feature + 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(TestIcinga2Feature, 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_enable_feature(self): + """Check that result is changed.""" + set_module_args({ + 'name': 'api', + }) + 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: + icinga2_feature.main() + self.assertTrue(result.exception.args[0]['changed']) + + self.assertEqual(run_command.call_count, 2) + self.assertEqual(run_command.call_args[0][0][-1], 'api') + + def test_enable_feature_with_check_mode(self): + """Check that result is changed in check mode.""" + set_module_args({ + 'name': 'api', + '_ansible_check_mode': True, + }) + 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: + icinga2_feature.main() + self.assertTrue(result.exception.args[0]['changed']) + + self.assertEqual(run_command.call_count, 1) + + def test_disable_feature(self): + """Check that result is changed.""" + set_module_args({ + 'name': 'api', + 'state': 'absent' + }) + 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: + icinga2_feature.main() + self.assertTrue(result.exception.args[0]['changed']) + + self.assertEqual(run_command.call_count, 2) + self.assertEqual(run_command.call_args[0][0][-1], 'api') + + def test_disable_feature_with_check_mode(self): + """Check that result is changed in check mode.""" + set_module_args({ + 'name': 'api', + 'state': 'absent', + '_ansible_check_mode': True, + }) + 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: + icinga2_feature.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_ipa_otpconfig.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otpconfig.py new file mode 100644 index 000000000..718359a30 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otpconfig.py @@ -0,0 +1,407 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2020, 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 call, patch +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + +from ansible_collections.community.general.plugins.modules import ipa_otpconfig + + +@contextmanager +def patch_ipa(**kwargs): + """Mock context manager for patching the methods in OTPConfigIPAClient 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 = ipa_otpconfig.OTPConfigIPAClient + with patch.object(obj, 'login') as mock_login: + with patch.object(obj, '_post_json', **kwargs) as mock_post: + yield mock_login, mock_post + + +class TestIPAOTPConfig(ModuleTestCase): + def setUp(self): + super(TestIPAOTPConfig, self).setUp() + self.module = ipa_otpconfig + + def _test_base(self, module_args, return_value, mock_calls, changed): + """Base function that's called by all the other test functions + + module_args (dict): + Arguments passed to the module + + return_value (dict): + Mocked return value of OTPConfigIPAClient.otpconfig_show, as returned by the IPA API. + This should be set to the current state. It will be changed to the desired state using the above arguments. + (Technically, this is the return value of _post_json, but it's only checked by otpconfig_show). + + mock_calls (list/tuple of dicts): + List of calls made to OTPConfigIPAClient._post_json, in order. + _post_json is called by all of the otpconfig_* methods of the class. + Pass an empty list if no calls are expected. + + changed (bool): + Whether or not the module is supposed to be marked as changed + """ + set_module_args(module_args) + + # Run the module + with patch_ipa(return_value=return_value) as (mock_login, mock_post): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify that the calls to _post_json match what is expected + expected_call_count = len(mock_calls) + if expected_call_count > 1: + # Convert the call dicts to unittest.mock.call instances because `assert_has_calls` only accepts them + converted_calls = [] + for call_dict in mock_calls: + converted_calls.append(call(**call_dict)) + + mock_post.assert_has_calls(converted_calls) + self.assertEqual(len(mock_post.mock_calls), expected_call_count) + elif expected_call_count == 1: + mock_post.assert_called_once_with(**mock_calls[0]) + else: # expected_call_count is 0 + mock_post.assert_not_called() + + # Verify that the module's changed status matches what is expected + self.assertIs(exec_info.exception.args[0]['changed'], changed) + + def test_set_all_no_adjustment(self): + """Set values requiring no adjustment""" + module_args = { + 'ipatokentotpauthwindow': 11, + 'ipatokentotpsyncwindow': 12, + 'ipatokenhotpauthwindow': 13, + 'ipatokenhotpsyncwindow': 14 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_all_aliases_no_adjustment(self): + """Set values requiring no adjustment on all using aliases values""" + module_args = { + 'totpauthwindow': 11, + 'totpsyncwindow': 12, + 'hotpauthwindow': 13, + 'hotpsyncwindow': 14 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_totp_auth_window_no_adjustment(self): + """Set values requiring no adjustment on totpauthwindow""" + module_args = { + 'totpauthwindow': 11 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_totp_sync_window_no_adjustment(self): + """Set values requiring no adjustment on totpsyncwindow""" + module_args = { + 'totpsyncwindow': 12 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_hotp_auth_window_no_adjustment(self): + """Set values requiring no adjustment on hotpauthwindow""" + module_args = { + 'hotpauthwindow': 13 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_hotp_sync_window_no_adjustment(self): + """Set values requiring no adjustment on hotpsyncwindow""" + module_args = { + 'hotpsyncwindow': 14 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_totp_auth_window(self): + """Set values requiring adjustment on totpauthwindow""" + module_args = { + 'totpauthwindow': 10 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_mod', + 'name': None, + 'item': {'ipatokentotpauthwindow': '10'} + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_totp_sync_window(self): + """Set values requiring adjustment on totpsyncwindow""" + module_args = { + 'totpsyncwindow': 10 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_mod', + 'name': None, + 'item': {'ipatokentotpsyncwindow': '10'} + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_hotp_auth_window(self): + """Set values requiring adjustment on hotpauthwindow""" + module_args = { + 'hotpauthwindow': 10 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_mod', + 'name': None, + 'item': {'ipatokenhotpauthwindow': '10'} + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_hotp_sync_window(self): + """Set values requiring adjustment on hotpsyncwindow""" + module_args = { + 'hotpsyncwindow': 10 + } + return_value = { + 'ipatokentotpauthwindow': ['11'], + 'ipatokentotpsyncwindow': ['12'], + 'ipatokenhotpauthwindow': ['13'], + 'ipatokenhotpsyncwindow': ['14']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_mod', + 'name': None, + 'item': {'ipatokenhotpsyncwindow': '10'} + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_set_all(self): + """Set values requiring adjustment on all""" + module_args = { + 'ipatokentotpauthwindow': 11, + 'ipatokentotpsyncwindow': 12, + 'ipatokenhotpauthwindow': 13, + 'ipatokenhotpsyncwindow': 14 + } + return_value = { + 'ipatokentotpauthwindow': ['1'], + 'ipatokentotpsyncwindow': ['2'], + 'ipatokenhotpauthwindow': ['3'], + 'ipatokenhotpsyncwindow': ['4']} + mock_calls = ( + { + 'method': 'otpconfig_show', + 'name': None + }, + { + 'method': 'otpconfig_mod', + 'name': None, + 'item': {'ipatokentotpauthwindow': '11', + 'ipatokentotpsyncwindow': '12', + 'ipatokenhotpauthwindow': '13', + 'ipatokenhotpsyncwindow': '14'} + }, + { + 'method': 'otpconfig_show', + 'name': None + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_fail_post(self): + """Fail due to an exception raised from _post_json""" + set_module_args({ + 'ipatokentotpauthwindow': 11, + 'ipatokentotpsyncwindow': 12, + 'ipatokenhotpauthwindow': 13, + 'ipatokenhotpsyncwindow': 14 + }) + + with patch_ipa(side_effect=Exception('ERROR MESSAGE')) as (mock_login, mock_post): + with self.assertRaises(AnsibleFailJson) as exec_info: + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['msg'], 'ERROR MESSAGE') + + +if __name__ == '__main__': + unittest.main() 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 new file mode 100644 index 000000000..c06e19c3b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py @@ -0,0 +1,496 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2020, 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 call, patch +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + +from ansible_collections.community.general.plugins.modules import ipa_otptoken + + +@contextmanager +def patch_ipa(**kwargs): + """Mock context manager for patching the methods in OTPTokenIPAClient 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 = ipa_otptoken.OTPTokenIPAClient + with patch.object(obj, 'login') as mock_login: + with patch.object(obj, '_post_json', **kwargs) as mock_post: + yield mock_login, mock_post + + +class TestIPAOTPToken(ModuleTestCase): + def setUp(self): + super(TestIPAOTPToken, self).setUp() + self.module = ipa_otptoken + + def _test_base(self, module_args, return_value, mock_calls, changed): + """Base function that's called by all the other test functions + + module_args (dict): + Arguments passed to the module + + return_value (dict): + Mocked return value of OTPTokenIPAClient.otptoken_show, as returned by the IPA API. + This should be set to the current state. It will be changed to the desired state using the above arguments. + (Technically, this is the return value of _post_json, but it's only checked by otptoken_show). + + mock_calls (list/tuple of dicts): + List of calls made to OTPTokenIPAClient._post_json, in order. + _post_json is called by all of the otptoken_* methods of the class. + Pass an empty list if no calls are expected. + + changed (bool): + Whether or not the module is supposed to be marked as changed + """ + set_module_args(module_args) + + # Run the module + with patch_ipa(return_value=return_value) as (mock_login, mock_post): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify that the calls to _post_json match what is expected + expected_call_count = len(mock_calls) + if expected_call_count > 1: + # Convert the call dicts to unittest.mock.call instances because `assert_has_calls` only accepts them + converted_calls = [] + for call_dict in mock_calls: + converted_calls.append(call(**call_dict)) + + mock_post.assert_has_calls(converted_calls) + self.assertEqual(len(mock_post.mock_calls), expected_call_count) + elif expected_call_count == 1: + mock_post.assert_called_once_with(**mock_calls[0]) + else: # expected_call_count is 0 + mock_post.assert_not_called() + + # Verify that the module's changed status matches what is expected + self.assertIs(exec_info.exception.args[0]['changed'], changed) + + def test_add_new_all_default(self): + """Add a new OTP with all default values""" + module_args = { + 'uniqueid': 'NewToken1' + } + return_value = {} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_add', + 'name': 'NewToken1', + 'item': {'ipatokendisabled': 'FALSE', + 'all': True} + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_add_new_all_default_with_aliases(self): + """Add a new OTP with all default values using alias values""" + module_args = { + 'name': 'NewToken1' + } + return_value = {} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_add', + 'name': 'NewToken1', + 'item': {'ipatokendisabled': 'FALSE', + 'all': True} + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_add_new_all_specified(self): + """Add a new OTP with all default values""" + module_args = { + 'uniqueid': 'NewToken1', + 'otptype': 'hotp', + 'secretkey': 'VGVzdFNlY3JldDE=', + 'description': 'Test description', + 'owner': 'pinky', + 'enabled': True, + 'notbefore': '20200101010101', + 'notafter': '20900101010101', + 'vendor': 'Acme', + 'model': 'ModelT', + 'serial': 'Number1', + 'state': 'present', + 'algorithm': 'sha256', + 'digits': 6, + 'offset': 10, + 'interval': 30, + 'counter': 30, + } + return_value = {} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_add', + 'name': 'NewToken1', + 'item': {'type': 'HOTP', + 'ipatokenotpkey': 'KRSXG5CTMVRXEZLUGE======', + 'description': 'Test description', + 'ipatokenowner': 'pinky', + 'ipatokendisabled': 'FALSE', + 'ipatokennotbefore': '20200101010101Z', + 'ipatokennotafter': '20900101010101Z', + 'ipatokenvendor': 'Acme', + 'ipatokenmodel': 'ModelT', + 'ipatokenserial': 'Number1', + 'ipatokenotpalgorithm': 'sha256', + 'ipatokenotpdigits': '6', + 'ipatokentotpclockoffset': '10', + 'ipatokentotptimestep': '30', + 'ipatokenhotpcounter': '30', + 'all': True} + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_already_existing_no_change_all_specified(self): + """Add a new OTP with all values specified but needing no change""" + module_args = { + 'uniqueid': 'NewToken1', + 'otptype': 'hotp', + 'secretkey': 'VGVzdFNlY3JldDE=', + 'description': 'Test description', + 'owner': 'pinky', + 'enabled': True, + 'notbefore': '20200101010101', + 'notafter': '20900101010101', + 'vendor': 'Acme', + 'model': 'ModelT', + 'serial': 'Number1', + 'state': 'present', + 'algorithm': 'sha256', + 'digits': 6, + 'offset': 10, + 'interval': 30, + 'counter': 30, + } + return_value = {'ipatokenuniqueid': 'NewToken1', + 'type': 'HOTP', + 'ipatokenotpkey': [{'__base64__': 'VGVzdFNlY3JldDE='}], + 'description': ['Test description'], + 'ipatokenowner': ['pinky'], + 'ipatokendisabled': ['FALSE'], + 'ipatokennotbefore': ['20200101010101Z'], + 'ipatokennotafter': ['20900101010101Z'], + 'ipatokenvendor': ['Acme'], + 'ipatokenmodel': ['ModelT'], + 'ipatokenserial': ['Number1'], + 'ipatokenotpalgorithm': ['sha256'], + 'ipatokenotpdigits': ['6'], + 'ipatokentotpclockoffset': ['10'], + 'ipatokentotptimestep': ['30'], + 'ipatokenhotpcounter': ['30']} + mock_calls = [ + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + } + ] + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_already_existing_one_change_all_specified(self): + """Modify an existing OTP with one value specified needing change""" + module_args = { + 'uniqueid': 'NewToken1', + 'otptype': 'hotp', + 'secretkey': 'VGVzdFNlY3JldDE=', + 'description': 'Test description', + 'owner': 'brain', + 'enabled': True, + 'notbefore': '20200101010101', + 'notafter': '20900101010101', + 'vendor': 'Acme', + 'model': 'ModelT', + 'serial': 'Number1', + 'state': 'present', + 'algorithm': 'sha256', + 'digits': 6, + 'offset': 10, + 'interval': 30, + 'counter': 30, + } + return_value = {'ipatokenuniqueid': 'NewToken1', + 'type': 'HOTP', + 'ipatokenotpkey': [{'__base64__': 'VGVzdFNlY3JldDE='}], + 'description': ['Test description'], + 'ipatokenowner': ['pinky'], + 'ipatokendisabled': ['FALSE'], + 'ipatokennotbefore': ['20200101010101Z'], + 'ipatokennotafter': ['20900101010101Z'], + 'ipatokenvendor': ['Acme'], + 'ipatokenmodel': ['ModelT'], + 'ipatokenserial': ['Number1'], + 'ipatokenotpalgorithm': ['sha256'], + 'ipatokenotpdigits': ['6'], + 'ipatokentotpclockoffset': ['10'], + 'ipatokentotptimestep': ['30'], + 'ipatokenhotpcounter': ['30']} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_mod', + 'name': 'NewToken1', + 'item': {'description': 'Test description', + 'ipatokenowner': 'brain', + 'ipatokendisabled': 'FALSE', + 'ipatokennotbefore': '20200101010101Z', + 'ipatokennotafter': '20900101010101Z', + 'ipatokenvendor': 'Acme', + 'ipatokenmodel': 'ModelT', + 'ipatokenserial': 'Number1', + 'all': True} + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_already_existing_all_valid_change_all_specified(self): + """Modify an existing OTP with all valid values specified needing change""" + module_args = { + 'uniqueid': 'NewToken1', + 'otptype': 'hotp', + 'secretkey': 'VGVzdFNlY3JldDE=', + 'description': 'New Test description', + 'owner': 'pinky', + 'enabled': False, + 'notbefore': '20200101010102', + 'notafter': '20900101010102', + 'vendor': 'NewAcme', + 'model': 'NewModelT', + 'serial': 'Number2', + 'state': 'present', + 'algorithm': 'sha256', + 'digits': 6, + 'offset': 10, + 'interval': 30, + 'counter': 30, + } + return_value = {'ipatokenuniqueid': 'NewToken1', + 'type': 'HOTP', + 'ipatokenotpkey': [{'__base64__': 'VGVzdFNlY3JldDE='}], + 'description': ['Test description'], + 'ipatokenowner': ['pinky'], + 'ipatokendisabled': ['FALSE'], + 'ipatokennotbefore': ['20200101010101Z'], + 'ipatokennotafter': ['20900101010101Z'], + 'ipatokenvendor': ['Acme'], + 'ipatokenmodel': ['ModelT'], + 'ipatokenserial': ['Number1'], + 'ipatokenotpalgorithm': ['sha256'], + 'ipatokenotpdigits': ['6'], + 'ipatokentotpclockoffset': ['10'], + 'ipatokentotptimestep': ['30'], + 'ipatokenhotpcounter': ['30']} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_mod', + 'name': 'NewToken1', + 'item': {'description': 'New Test description', + 'ipatokenowner': 'pinky', + 'ipatokendisabled': 'TRUE', + 'ipatokennotbefore': '20200101010102Z', + 'ipatokennotafter': '20900101010102Z', + 'ipatokenvendor': 'NewAcme', + 'ipatokenmodel': 'NewModelT', + 'ipatokenserial': 'Number2', + 'all': True} + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_delete_existing_token(self): + """Delete an existing OTP""" + module_args = { + 'uniqueid': 'NewToken1', + 'state': 'absent' + } + return_value = {'ipatokenuniqueid': 'NewToken1', + 'type': 'HOTP', + 'ipatokenotpkey': [{'__base64__': 'KRSXG5CTMVRXEZLUGE======'}], + 'description': ['Test description'], + 'ipatokenowner': ['pinky'], + 'ipatokendisabled': ['FALSE'], + 'ipatokennotbefore': ['20200101010101Z'], + 'ipatokennotafter': ['20900101010101Z'], + 'ipatokenvendor': ['Acme'], + 'ipatokenmodel': ['ModelT'], + 'ipatokenserial': ['Number1'], + 'ipatokenotpalgorithm': ['sha256'], + 'ipatokenotpdigits': ['6'], + 'ipatokentotpclockoffset': ['10'], + 'ipatokentotptimestep': ['30'], + 'ipatokenhotpcounter': ['30']} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_del', + 'name': 'NewToken1' + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_disable_existing_token(self): + """Disable an existing OTP""" + module_args = { + 'uniqueid': 'NewToken1', + 'otptype': 'hotp', + 'enabled': False + } + return_value = {'ipatokenuniqueid': 'NewToken1', + 'type': 'HOTP', + 'ipatokenotpkey': [{'__base64__': 'KRSXG5CTMVRXEZLUGE======'}], + 'description': ['Test description'], + 'ipatokenowner': ['pinky'], + 'ipatokendisabled': ['FALSE'], + 'ipatokennotbefore': ['20200101010101Z'], + 'ipatokennotafter': ['20900101010101Z'], + 'ipatokenvendor': ['Acme'], + 'ipatokenmodel': ['ModelT'], + 'ipatokenserial': ['Number1'], + 'ipatokenotpalgorithm': ['sha256'], + 'ipatokenotpdigits': ['6'], + 'ipatokentotpclockoffset': ['10'], + 'ipatokentotptimestep': ['30'], + 'ipatokenhotpcounter': ['30']} + mock_calls = ( + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + }, + { + 'method': 'otptoken_mod', + 'name': 'NewToken1', + 'item': {'ipatokendisabled': 'TRUE', + 'all': True} + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_delete_not_existing_token(self): + """Delete a OTP that does not exist""" + module_args = { + 'uniqueid': 'NewToken1', + 'state': 'absent' + } + return_value = {} + + mock_calls = [ + { + 'method': 'otptoken_find', + 'name': None, + 'item': {'all': True, + 'ipatokenuniqueid': 'NewToken1', + 'timelimit': '0', + 'sizelimit': '0'} + } + ] + + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_fail_post(self): + """Fail due to an exception raised from _post_json""" + set_module_args({ + 'uniqueid': 'NewToken1' + }) + + with patch_ipa(side_effect=Exception('ERROR MESSAGE')) as (mock_login, mock_post): + with self.assertRaises(AnsibleFailJson) as exec_info: + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['msg'], 'ERROR MESSAGE') + + +if __name__ == '__main__': + unittest.main() 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 new file mode 100644 index 000000000..b45c566fc --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py @@ -0,0 +1,614 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2020, 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 call, patch +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + +from ansible_collections.community.general.plugins.modules import ipa_pwpolicy + + +@contextmanager +def patch_ipa(**kwargs): + """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 = ipa_pwpolicy.PwPolicyIPAClient + with patch.object(obj, 'login') as mock_login: + with patch.object(obj, '_post_json', **kwargs) as mock_post: + yield mock_login, mock_post + + +class TestIPAPwPolicy(ModuleTestCase): + def setUp(self): + super(TestIPAPwPolicy, self).setUp() + self.module = ipa_pwpolicy + + def _test_base(self, module_args, return_value, mock_calls, changed): + """Base function that's called by all the other test functions + + module_args (dict): + Arguments passed to the module + + return_value (dict): + Mocked return value of PwPolicyIPAClient.pwpolicy_find, as returned by the IPA API. + This should be set to the current state. It will be changed to the desired state using the above arguments. + (Technically, this is the return value of _post_json, but it's only checked by pwpolicy_find). + An empty dict means that the policy doesn't exist. + + mock_calls (list/tuple of dicts): + List of calls made to PwPolicyIPAClient._post_json, in order. + _post_json is called by all of the pwpolicy_* methods of the class. + Pass an empty list if no calls are expected. + + changed (bool): + Whether or not the module is supposed to be marked as changed + """ + set_module_args(module_args) + + # Run the module + with patch_ipa(return_value=return_value) as (mock_login, mock_post): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify that the calls to _post_json match what is expected + expected_call_count = len(mock_calls) + if expected_call_count > 1: + # Convert the call dicts to unittest.mock.call instances because `assert_has_calls` only accepts them + converted_calls = [] + for call_dict in mock_calls: + converted_calls.append(call(**call_dict)) + + mock_post.assert_has_calls(converted_calls) + self.assertEqual(len(mock_post.mock_calls), expected_call_count) + elif expected_call_count == 1: + mock_post.assert_called_once_with(**mock_calls[0]) + else: # expected_call_count is 0 + mock_post.assert_not_called() + + # Verify that the module's changed status matches what is expected + self.assertIs(exec_info.exception.args[0]['changed'], changed) + + def test_add(self): + """Add a new policy""" + module_args = { + 'group': 'admins', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '90', + 'minpwdlife': '1', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '16', + 'maxfailcount': '6', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = {} + mock_calls = ( + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'admins' + } + }, + { + 'method': 'pwpolicy_add', + 'name': 'admins', + 'item': { + 'cospriority': '10', + 'krbmaxpwdlife': '90', + 'krbminpwdlife': '1', + 'krbpwdhistorylength': '8', + 'krbpwdmindiffchars': '3', + 'krbpwdminlength': '16', + 'krbpwdmaxfailure': '6', + 'krbpwdfailurecountinterval': '60', + 'krbpwdlockoutduration': '600' + } + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_aliases(self): + """Same as test_add, but uses the `name` alias for the `group` option""" + module_args = { + 'name': 'admins', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '90', + 'minpwdlife': '1', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '16', + 'maxfailcount': '6', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = {} + mock_calls = ( + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'admins' + } + }, + { + 'method': 'pwpolicy_add', + 'name': 'admins', + 'item': { + 'cospriority': '10', + 'krbmaxpwdlife': '90', + 'krbminpwdlife': '1', + 'krbpwdhistorylength': '8', + 'krbpwdmindiffchars': '3', + 'krbpwdminlength': '16', + 'krbpwdmaxfailure': '6', + 'krbpwdfailurecountinterval': '60', + 'krbpwdlockoutduration': '600' + } + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_mod_different_args(self): + """Policy exists, but some of the args are different and need to be modified""" + module_args = { + 'group': 'sysops', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '60', + 'minpwdlife': '24', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '12', + 'maxfailcount': '8', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = { + 'cn': ['sysops'], + 'cospriority': ['10'], + 'krbmaxpwdlife': ['90'], + 'krbminpwdlife': ['1'], + 'krbpwdhistorylength': ['8'], + 'krbpwdmindiffchars': ['3'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'krbpwdfailurecountinterval': ['60'], + 'krbpwdlockoutduration': ['600'], + 'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = ( + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'sysops' + } + }, + { + 'method': 'pwpolicy_mod', + 'name': 'sysops', + 'item': { + 'cospriority': '10', + 'krbmaxpwdlife': '60', + 'krbminpwdlife': '24', + 'krbpwdhistorylength': '8', + 'krbpwdmindiffchars': '3', + 'krbpwdminlength': '12', + 'krbpwdmaxfailure': '8', + 'krbpwdfailurecountinterval': '60', + 'krbpwdlockoutduration': '600' + } + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_mod_missing_args(self): + """Policy exists, but some of the args aren't set, so need to be added""" + module_args = { + 'group': 'sysops', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '90', + 'minpwdlife': '1', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '16', + 'maxfailcount': '6', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = { + 'cn': ['sysops'], + 'cospriority': ['10'], + 'krbmaxpwdlife': ['90'], + 'krbpwdhistorylength': ['8'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = ( + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'sysops' + } + }, + { + 'method': 'pwpolicy_mod', + 'name': 'sysops', + 'item': { + 'cospriority': '10', + 'krbmaxpwdlife': '90', + 'krbminpwdlife': '1', + 'krbpwdhistorylength': '8', + 'krbpwdmindiffchars': '3', + 'krbpwdminlength': '16', + 'krbpwdmaxfailure': '6', + 'krbpwdfailurecountinterval': '60', + 'krbpwdlockoutduration': '600' + } + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_del(self): + """Policy exists, and state is absent. Needs to be deleted""" + module_args = { + 'group': 'sysops', + 'state': 'absent', + # other arguments are ignored when state is `absent` + 'priority': '10', + 'maxpwdlife': '90', + 'historylength': '8', + 'minlength': '16', + 'maxfailcount': '6' + } + return_value = { + 'cn': ['sysops'], + 'cospriority': ['10'], + 'krbmaxpwdlife': ['90'], + 'krbpwdhistorylength': ['8'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = ( + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'sysops' + } + }, + { + 'method': 'pwpolicy_del', + 'name': 'sysops', + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_no_change(self): + """Policy already exists. No changes needed""" + module_args = { + 'group': 'admins', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '90', + 'minpwdlife': '1', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '16', + 'maxfailcount': '6', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = { + 'cn': ['admins'], + 'cospriority': ['10'], + 'krbmaxpwdlife': ['90'], + 'krbminpwdlife': ['1'], + 'krbpwdhistorylength': ['8'], + 'krbpwdmindiffchars': ['3'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'krbpwdfailurecountinterval': ['60'], + 'krbpwdlockoutduration': ['600'], + 'dn': 'cn=admins,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = [ + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'admins' + } + } + ] + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_del_no_change(self): + """Policy doesn't exist, and state is absent. No change needed""" + module_args = { + 'group': 'sysops', + 'state': 'absent', + # other arguments are ignored when state is `absent` + 'priority': '10', + 'maxpwdlife': '90', + 'historylength': '8', + 'minlength': '16', + 'maxfailcount': '6' + } + return_value = {} + mock_calls = [ + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'sysops' + } + } + ] + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_global(self): + """Modify the global policy""" + module_args = { + 'maxpwdlife': '60', + 'minpwdlife': '24', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '12', + 'maxfailcount': '8', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = { + 'cn': ['global_policy'], + 'krbmaxpwdlife': ['90'], + 'krbminpwdlife': ['1'], + 'krbpwdmindiffchars': ['3'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'krbpwdfailurecountinterval': ['60'], + 'krbpwdlockoutduration': ['600'], + 'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = ( + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'global_policy' + } + }, + { + 'method': 'pwpolicy_mod', + 'name': None, + 'item': { + 'krbmaxpwdlife': '60', + 'krbminpwdlife': '24', + 'krbpwdhistorylength': '8', + 'krbpwdmindiffchars': '3', + 'krbpwdminlength': '12', + 'krbpwdmaxfailure': '8', + 'krbpwdfailurecountinterval': '60', + 'krbpwdlockoutduration': '600' + } + } + ) + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_global_no_change(self): + """Global policy already matches the given arguments. No change needed""" + module_args = { + 'maxpwdlife': '90', + 'minpwdlife': '1', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '16', + 'maxfailcount': '6', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = { + 'cn': ['global_policy'], + 'krbmaxpwdlife': ['90'], + 'krbminpwdlife': ['1'], + 'krbpwdhistorylength': ['8'], + 'krbpwdmindiffchars': ['3'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'krbpwdfailurecountinterval': ['60'], + 'krbpwdlockoutduration': ['600'], + 'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = [ + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'global_policy' + } + } + ] + changed = False + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_check_add(self): + """Add a new policy in check mode. pwpolicy_add shouldn't be called""" + module_args = { + '_ansible_check_mode': True, + 'group': 'admins', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '90', + 'minpwdlife': '1', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '16', + 'maxfailcount': '6', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = {} + mock_calls = [ + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'admins' + } + } + ] + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_check_mod(self): + """Modify a policy in check mode. pwpolicy_mod shouldn't be called""" + module_args = { + '_ansible_check_mode': True, + 'group': 'sysops', + 'state': 'present', + 'priority': '10', + 'maxpwdlife': '60', + 'minpwdlife': '24', + 'historylength': '8', + 'minclasses': '3', + 'minlength': '12', + 'maxfailcount': '8', + 'failinterval': '60', + 'lockouttime': '600' + } + return_value = { + 'cn': ['sysops'], + 'cospriority': ['10'], + 'krbmaxpwdlife': ['90'], + 'krbminpwdlife': ['1'], + 'krbpwdhistorylength': ['8'], + 'krbpwdmindiffchars': ['3'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'krbpwdfailurecountinterval': ['60'], + 'krbpwdlockoutduration': ['600'], + 'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = [ + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'sysops' + } + } + ] + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_check_del(self): + """Delete a policy in check mode. pwpolicy_del shouldn't be called""" + module_args = { + '_ansible_check_mode': True, + 'group': 'sysops', + 'state': 'absent' + } + return_value = { + 'cn': ['sysops'], + 'cospriority': ['10'], + 'krbmaxpwdlife': ['90'], + 'krbpwdhistorylength': ['8'], + 'krbpwdminlength': ['16'], + 'krbpwdmaxfailure': ['6'], + 'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com', + 'objectclass': ['top', 'nscontainer', 'krbpwdpolicy'] + } + mock_calls = [ + { + 'method': 'pwpolicy_find', + 'name': None, + 'item': { + 'all': True, + 'cn': 'sysops' + } + } + ] + changed = True + + self._test_base(module_args, return_value, mock_calls, changed) + + def test_fail_post(self): + """Fail due to an exception raised from _post_json""" + set_module_args({ + 'group': 'admins', + 'state': 'absent' + }) + + with patch_ipa(side_effect=Exception('ERROR MESSAGE')) as (mock_login, mock_post): + with self.assertRaises(AnsibleFailJson) as exec_info: + self.module.main() + + self.assertEqual(exec_info.exception.args[0]['msg'], 'ERROR MESSAGE') + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_java_keystore.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_java_keystore.py new file mode 100644 index 000000000..b2e70404a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_java_keystore.py @@ -0,0 +1,422 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2018, Ansible Project +# Copyright (c) 2018, Abhijeet Kasurde +# +# 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 + +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ModuleTestCase, set_module_args +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.compat.mock import Mock +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.modules.java_keystore import JavaKeystore + + +module_argument_spec = dict( + name=dict(type='str', required=True), + dest=dict(type='path', required=True), + certificate=dict(type='str', no_log=True), + certificate_path=dict(type='path'), + private_key=dict(type='str', no_log=True), + private_key_path=dict(type='path', no_log=False), + private_key_passphrase=dict(type='str', no_log=True), + password=dict(type='str', required=True, no_log=True), + ssl_backend=dict(type='str', default='openssl', choices=['openssl', 'cryptography']), + keystore_type=dict(type='str', choices=['jks', 'pkcs12']), + force=dict(type='bool', default=False), +) +module_supports_check_mode = True +module_choose_between = (['certificate', 'certificate_path'], + ['private_key', 'private_key_path']) + + +class TestCreateJavaKeystore(ModuleTestCase): + """Test the creation of a Java keystore.""" + + def setUp(self): + """Setup.""" + super(TestCreateJavaKeystore, self).setUp() + + orig_exists = os.path.exists + self.mock_create_file = patch('ansible_collections.community.general.plugins.modules.java_keystore.create_file') + self.mock_create_path = patch('ansible_collections.community.general.plugins.modules.java_keystore.create_path') + self.mock_current_type = patch('ansible_collections.community.general.plugins.modules.java_keystore.JavaKeystore.current_type') + self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command') + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + self.mock_preserved_copy = patch('ansible.module_utils.basic.AnsibleModule.preserved_copy') + self.mock_atomic_move = patch('ansible.module_utils.basic.AnsibleModule.atomic_move') + self.mock_os_path_exists = patch('os.path.exists', + side_effect=lambda path: True if path == '/path/to/keystore.jks' else orig_exists(path)) + self.mock_selinux_context = patch('ansible.module_utils.basic.AnsibleModule.selinux_context', + side_effect=lambda path: ['unconfined_u', 'object_r', 'user_home_t', 's0']) + self.mock_is_special_selinux_path = patch('ansible.module_utils.basic.AnsibleModule.is_special_selinux_path', + side_effect=lambda path: (False, None)) + self.run_command = self.mock_run_command.start() + self.get_bin_path = self.mock_get_bin_path.start() + self.preserved_copy = self.mock_preserved_copy.start() + self.atomic_move = self.mock_atomic_move.start() + self.create_file = self.mock_create_file.start() + self.create_path = self.mock_create_path.start() + self.current_type = self.mock_current_type.start() + self.selinux_context = self.mock_selinux_context.start() + self.is_special_selinux_path = self.mock_is_special_selinux_path.start() + self.os_path_exists = self.mock_os_path_exists.start() + + def tearDown(self): + """Teardown.""" + super(TestCreateJavaKeystore, self).tearDown() + self.mock_create_file.stop() + self.mock_create_path.stop() + self.mock_current_type.stop() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + self.mock_preserved_copy.stop() + self.mock_atomic_move.stop() + self.mock_selinux_context.stop() + self.mock_is_special_selinux_path.stop() + self.mock_os_path_exists.stop() + + def test_create_jks_success(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='test', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + with patch('os.remove', return_value=True): + self.create_path.side_effect = ['/tmp/tmpgrzm2ah7'] + self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp', ''] + self.run_command.side_effect = [(0, '', ''), (0, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + assert jks.create() == { + 'changed': True, + 'cmd': ["keytool", "-importkeystore", + "-destkeystore", "/path/to/keystore.jks", + "-srckeystore", "/tmp/tmpgrzm2ah7", "-srcstoretype", "pkcs12", "-alias", "test", + "-noprompt"], + 'msg': '', + 'rc': 0 + } + + def test_create_jks_keypass_fail_export_pkcs12(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + private_key_passphrase='passphrase-foo', + dest='/path/to/keystore.jks', + name='test', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + module.exit_json = Mock() + module.fail_json = Mock() + + with patch('os.remove', return_value=True): + self.create_path.side_effect = ['/tmp/tmp1cyp12xa'] + self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c', ''] + self.run_command.side_effect = [(1, '', 'Oops'), (0, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.create() + module.fail_json.assert_called_once_with( + cmd=["openssl", "pkcs12", "-export", "-name", "test", + "-in", "/tmp/tmpvalcrt32", + "-inkey", "/tmp/tmpwh4key0c", + "-out", "/tmp/tmp1cyp12xa", + "-passout", "stdin", + "-passin", "stdin"], + msg='', + err='Oops', + rc=1 + ) + + def test_create_jks_fail_export_pkcs12(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='test', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + module.exit_json = Mock() + module.fail_json = Mock() + + with patch('os.remove', return_value=True): + self.create_path.side_effect = ['/tmp/tmp1cyp12xa'] + self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c', ''] + self.run_command.side_effect = [(1, '', 'Oops'), (0, '', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.create() + module.fail_json.assert_called_once_with( + cmd=["openssl", "pkcs12", "-export", "-name", "test", + "-in", "/tmp/tmpvalcrt32", + "-inkey", "/tmp/tmpwh4key0c", + "-out", "/tmp/tmp1cyp12xa", + "-passout", "stdin"], + msg='', + err='Oops', + rc=1 + ) + + def test_create_jks_fail_import_key(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='test', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + module.exit_json = Mock() + module.fail_json = Mock() + + with patch('os.remove', return_value=True): + self.create_path.side_effect = ['/tmp/tmpgrzm2ah7'] + self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp', ''] + self.run_command.side_effect = [(0, '', ''), (1, '', 'Oops')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.create() + module.fail_json.assert_called_once_with( + cmd=["keytool", "-importkeystore", + "-destkeystore", "/path/to/keystore.jks", + "-srckeystore", "/tmp/tmpgrzm2ah7", "-srcstoretype", "pkcs12", "-alias", "test", + "-noprompt"], + msg='', + err='Oops', + rc=1 + ) + + +class TestCertChanged(ModuleTestCase): + """Test if the cert has changed.""" + + def setUp(self): + """Setup.""" + super(TestCertChanged, self).setUp() + self.mock_create_file = patch('ansible_collections.community.general.plugins.modules.java_keystore.create_file') + self.mock_current_type = patch('ansible_collections.community.general.plugins.modules.java_keystore.JavaKeystore.current_type') + self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command') + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + self.mock_preserved_copy = patch('ansible.module_utils.basic.AnsibleModule.preserved_copy') + self.mock_atomic_move = patch('ansible.module_utils.basic.AnsibleModule.atomic_move') + self.run_command = self.mock_run_command.start() + self.create_file = self.mock_create_file.start() + self.get_bin_path = self.mock_get_bin_path.start() + self.current_type = self.mock_current_type.start() + self.preserved_copy = self.mock_preserved_copy.start() + self.atomic_move = self.mock_atomic_move.start() + + def tearDown(self): + """Teardown.""" + super(TestCertChanged, self).tearDown() + self.mock_create_file.stop() + self.mock_current_type.stop() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + self.mock_preserved_copy.stop() + self.mock_atomic_move.stop() + + def test_cert_unchanged_same_fingerprint(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='foo', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + with patch('os.remove', return_value=True): + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: abcd:1234:efgh', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + self.current_type.side_effect = ['jks'] + jks = JavaKeystore(module) + result = jks.cert_changed() + self.assertFalse(result, 'Fingerprint is identical') + + def test_cert_changed_fingerprint_mismatch(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='foo', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + with patch('os.remove', return_value=True): + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: wxyz:9876:stuv', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + self.current_type.side_effect = ['jks'] + jks = JavaKeystore(module) + result = jks.cert_changed() + self.assertTrue(result, 'Fingerprint mismatch') + + def test_cert_changed_alias_does_not_exist(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='foo', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + with patch('os.remove', return_value=True): + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), + (1, 'keytool error: java.lang.Exception: Alias does not exist', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + result = jks.cert_changed() + self.assertTrue(result, 'Alias mismatch detected') + + def test_cert_changed_password_mismatch(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='foo', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + with patch('os.remove', return_value=True): + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), + (1, 'keytool error: java.io.IOException: Keystore password was incorrect', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + result = jks.cert_changed() + self.assertTrue(result, 'Password mismatch detected') + + def test_cert_changed_fail_read_cert(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='foo', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + module.exit_json = Mock() + module.fail_json = Mock() + + with patch('os.remove', return_value=True): + self.create_file.side_effect = ['/tmp/tmpdj6bvvme', ''] + self.run_command.side_effect = [(1, '', 'Oops'), (0, 'SHA256: wxyz:9876:stuv', '')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + self.current_type.side_effect = ['jks'] + jks = JavaKeystore(module) + jks.cert_changed() + module.fail_json.assert_called_once_with( + cmd=["openssl", "x509", "-noout", "-in", "/tmp/tmpdj6bvvme", "-fingerprint", "-sha256"], + msg='', + err='Oops', + rc=1 + ) + + def test_cert_changed_fail_read_keystore(self): + set_module_args(dict( + certificate='cert-foo', + private_key='private-foo', + dest='/path/to/keystore.jks', + name='foo', + password='changeit' + )) + + module = AnsibleModule( + argument_spec=module_argument_spec, + supports_check_mode=module_supports_check_mode, + mutually_exclusive=module_choose_between, + required_one_of=module_choose_between + ) + + module.exit_json = Mock() + module.fail_json = Mock(return_value=True) + + with patch('os.remove', return_value=True): + self.create_file.side_effect = ['/tmp/placeholder', ''] + self.run_command.side_effect = [(0, 'foo: wxyz:9876:stuv', ''), (1, '', 'Oops')] + self.get_bin_path.side_effect = ['keytool', 'openssl', ''] + jks = JavaKeystore(module) + jks.cert_changed() + module.fail_json.assert_called_with( + cmd=["keytool", "-list", "-alias", "foo", "-keystore", "/path/to/keystore.jks", "-v"], + msg='', + err='Oops', + rc=1 + ) 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 new file mode 100644 index 000000000..44c6307ac --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py @@ -0,0 +1,224 @@ +# 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 + +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 NotFoundException(JenkinsException): + pass + + +class JenkinsBuildMock(): + def get_build_status(self): + try: + instance = JenkinsMock() + response = JenkinsMock.get_build_info(instance, 'host-delete', 1234) + return response + except jenkins.JenkinsException as e: + 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_job_info(self, name): + return { + "nextBuildNumber": 1234 + } + + 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)) + return { + "building": True, + "result": "SUCCESS" + } + + def build_job(self, *args): + return None + + def delete_build(self, name, build_number): + return None + + def stop_build(self, name, build_number): + return None + + +class JenkinsMockIdempotent(): + + def get_job_info(self, name): + return { + "nextBuildNumber": 1235 + } + + def get_build_info(self, name, build_number): + return { + "building": False, + "result": "ABORTED" + } + + def build_job(self, *args): + return None + + def delete_build(self, name, build_number): + raise jenkins.NotFoundException("job {0} number {1} does not exist".format(name, build_number)) + + def stop_build(self, name, build_number): + return None + + +class TestJenkinsBuild(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.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.main() + + @patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies') + def test_module_fail_when_missing_build_number(self, test_deps): + test_deps.return_value = None + with self.assertRaises(AnsibleFailJson): + set_module_args({ + "name": "required-if", + "state": "stopped" + }) + 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') + def test_module_create_build(self, jenkins_connection, test_deps): + test_deps.return_value = None + jenkins_connection.return_value = JenkinsMock() + + with self.assertRaises(AnsibleExitJson): + set_module_args({ + "name": "host-check", + "user": "abc", + "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') + def test_module_stop_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": "host-check", + "build_number": "1234", + "state": "stopped", + "user": "abc", + "token": "xyz" + }) + jenkins_build.main() + + self.assertTrue(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_stop_build_again(self, jenkins_connection, test_deps): + test_deps.return_value = None + jenkins_connection.return_value = JenkinsMockIdempotent() + + with self.assertRaises(AnsibleExitJson) as return_json: + set_module_args({ + "name": "host-check", + "build_number": "1234", + "state": "stopped", + "user": "abc", + "password": "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') + @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_build_status') + def test_module_delete_build(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): + set_module_args({ + "name": "host-delete", + "build_number": "1234", + "state": "absent", + "user": "abc", + "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') + def test_module_delete_build_again(self, jenkins_connection, test_deps): + test_deps.return_value = None + jenkins_connection.return_value = JenkinsMockIdempotent() + + with self.assertRaises(AnsibleFailJson): + set_module_args({ + "name": "host-delete", + "build_number": "1234", + "state": "absent", + "user": "abc", + "token": "xyz" + }) + jenkins_build.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_plugin.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_plugin.py new file mode 100644 index 000000000..194cc2d72 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_plugin.py @@ -0,0 +1,192 @@ +# 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 io import BytesIO + +from ansible_collections.community.general.plugins.modules.jenkins_plugin import JenkinsPlugin +from ansible.module_utils.common._collections_compat import Mapping + + +def pass_function(*args, **kwargs): + pass + + +GITHUB_DATA = {"url": u'https://api.github.com/repos/ansible/ansible', + "response": b""" +{ + "id": 3638964, + "name": "ansible", + "full_name": "ansible/ansible", + "owner": { + "login": "ansible", + "id": 1507452, + "avatar_url": "https://avatars2.githubusercontent.com/u/1507452?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ansible", + "html_url": "https://github.com/ansible", + "followers_url": "https://api.github.com/users/ansible/followers", + "following_url": "https://api.github.com/users/ansible/following{/other_user}", + "gists_url": "https://api.github.com/users/ansible/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ansible/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ansible/subscriptions", + "organizations_url": "https://api.github.com/users/ansible/orgs", + "repos_url": "https://api.github.com/users/ansible/repos", + "events_url": "https://api.github.com/users/ansible/events{/privacy}", + "received_events_url": "https://api.github.com/users/ansible/received_events", + "type": "Organization", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/ansible/ansible", + "description": "Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy.", + "fork": false, + "url": "https://api.github.com/repos/ansible/ansible", + "forks_url": "https://api.github.com/repos/ansible/ansible/forks", + "keys_url": "https://api.github.com/repos/ansible/ansible/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/ansible/ansible/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/ansible/ansible/teams", + "hooks_url": "https://api.github.com/repos/ansible/ansible/hooks", + "issue_events_url": "https://api.github.com/repos/ansible/ansible/issues/events{/number}", + "events_url": "https://api.github.com/repos/ansible/ansible/events", + "assignees_url": "https://api.github.com/repos/ansible/ansible/assignees{/user}", + "branches_url": "https://api.github.com/repos/ansible/ansible/branches{/branch}", + "tags_url": "https://api.github.com/repos/ansible/ansible/tags", + "blobs_url": "https://api.github.com/repos/ansible/ansible/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/ansible/ansible/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/ansible/ansible/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/ansible/ansible/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/ansible/ansible/statuses/{sha}", + "languages_url": "https://api.github.com/repos/ansible/ansible/languages", + "stargazers_url": "https://api.github.com/repos/ansible/ansible/stargazers", + "contributors_url": "https://api.github.com/repos/ansible/ansible/contributors", + "subscribers_url": "https://api.github.com/repos/ansible/ansible/subscribers", + "subscription_url": "https://api.github.com/repos/ansible/ansible/subscription", + "commits_url": "https://api.github.com/repos/ansible/ansible/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/ansible/ansible/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/ansible/ansible/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/ansible/ansible/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/ansible/ansible/contents/{+path}", + "compare_url": "https://api.github.com/repos/ansible/ansible/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/ansible/ansible/merges", + "archive_url": "https://api.github.com/repos/ansible/ansible/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/ansible/ansible/downloads", + "issues_url": "https://api.github.com/repos/ansible/ansible/issues{/number}", + "pulls_url": "https://api.github.com/repos/ansible/ansible/pulls{/number}", + "milestones_url": "https://api.github.com/repos/ansible/ansible/milestones{/number}", + "notifications_url": "https://api.github.com/repos/ansible/ansible/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/ansible/ansible/labels{/name}", + "releases_url": "https://api.github.com/repos/ansible/ansible/releases{/id}", + "deployments_url": "https://api.github.com/repos/ansible/ansible/deployments", + "created_at": "2012-03-06T14:58:02Z", + "updated_at": "2017-09-19T18:10:54Z", + "pushed_at": "2017-09-19T18:04:51Z", + "git_url": "git://github.com/ansible/ansible.git", + "ssh_url": "git@github.com:ansible/ansible.git", + "clone_url": "https://github.com/ansible/ansible.git", + "svn_url": "https://github.com/ansible/ansible", + "homepage": "https://www.ansible.com/", + "size": 91174, + "stargazers_count": 25552, + "watchers_count": 25552, + "language": "Python", + "has_issues": true, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "forks_count": 8893, + "mirror_url": null, + "open_issues_count": 4283, + "forks": 8893, + "open_issues": 4283, + "watchers": 25552, + "default_branch": "devel", + "organization": { + "login": "ansible", + "id": 1507452, + "avatar_url": "https://avatars2.githubusercontent.com/u/1507452?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/ansible", + "html_url": "https://github.com/ansible", + "followers_url": "https://api.github.com/users/ansible/followers", + "following_url": "https://api.github.com/users/ansible/following{/other_user}", + "gists_url": "https://api.github.com/users/ansible/gists{/gist_id}", + "starred_url": "https://api.github.com/users/ansible/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/ansible/subscriptions", + "organizations_url": "https://api.github.com/users/ansible/orgs", + "repos_url": "https://api.github.com/users/ansible/repos", + "events_url": "https://api.github.com/users/ansible/events{/privacy}", + "received_events_url": "https://api.github.com/users/ansible/received_events", + "type": "Organization", + "site_admin": false + }, + "network_count": 8893, + "subscribers_count": 1733 +} +""" + } + + +def test__get_json_data(mocker): + "test the json conversion of _get_url_data" + + timeout = 30 + params = { + 'url': GITHUB_DATA['url'], + 'timeout': timeout + } + module = mocker.Mock() + module.params = params + + JenkinsPlugin._csrf_enabled = pass_function + JenkinsPlugin._get_installed_plugins = pass_function + JenkinsPlugin._get_url_data = mocker.Mock() + JenkinsPlugin._get_url_data.return_value = BytesIO(GITHUB_DATA['response']) + jenkins_plugin = JenkinsPlugin(module) + + json_data = jenkins_plugin._get_json_data( + "{url}".format(url=GITHUB_DATA['url']), + 'CSRF') + + assert isinstance(json_data, Mapping) + + +def test__new_fallback_urls(mocker): + "test generation of new fallback URLs" + + params = { + "url": "http://fake.jenkins.server", + "timeout": 30, + "name": "test-plugin", + "version": "1.2.3", + "updates_url": ["https://some.base.url"], + "latest_plugins_url_segments": ["test_latest"], + "versioned_plugins_url_segments": ["ansible", "versioned_plugins"], + "update_json_url_segment": ["unreachable", "updates/update-center.json"], + } + module = mocker.Mock() + module.params = params + + JenkinsPlugin._csrf_enabled = pass_function + JenkinsPlugin._get_installed_plugins = pass_function + + jenkins_plugin = JenkinsPlugin(module) + + latest_urls = jenkins_plugin._get_latest_plugin_urls() + assert isInList(latest_urls, "https://some.base.url/test_latest/test-plugin.hpi") + versioned_urls = jenkins_plugin._get_versioned_plugin_urls() + assert isInList(versioned_urls, "https://some.base.url/versioned_plugins/test-plugin/1.2.3/test-plugin.hpi") + json_urls = jenkins_plugin._get_update_center_urls() + assert isInList(json_urls, "https://some.base.url/updates/update-center.json") + + +def isInList(l, i): + print("checking if %s in %s" % (i, l)) + for item in l: + if item == i: + return True + return False diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication.py new file mode 100644 index 000000000..aaa1fa9b1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication.py @@ -0,0 +1,623 @@ +# -*- 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 + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_authentication_flow_by_alias=None, copy_auth_flow=None, create_empty_auth_flow=None, + get_executions_representation=None, delete_authentication_flow_by_id=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.KeycloakAPI + with patch.object(obj, 'get_authentication_flow_by_alias', side_effect=get_authentication_flow_by_alias) \ + as mock_get_authentication_flow_by_alias: + with patch.object(obj, 'copy_auth_flow', side_effect=copy_auth_flow) \ + as mock_copy_auth_flow: + with patch.object(obj, 'create_empty_auth_flow', side_effect=create_empty_auth_flow) \ + as mock_create_empty_auth_flow: + with patch.object(obj, 'get_executions_representation', return_value=get_executions_representation) \ + as mock_get_executions_representation: + with patch.object(obj, 'delete_authentication_flow_by_id', side_effect=delete_authentication_flow_by_id) \ + as mock_delete_authentication_flow_by_id: + yield mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, \ + mock_get_executions_representation, mock_delete_authentication_flow_by_id + + +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 + + def test_create_auth_flow_from_copy(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'copyFrom': 'first broker login', + 'authenticationExecutions': [ + { + 'providerId': 'identity-provider-redirector', + 'requirement': 'ALTERNATIVE', + }, + ], + 'state': 'present', + } + return_value_auth_flow_before = [{}] + return_value_copied = [{ + 'id': '2ac059fc-c548-414f-9c9e-84d42bd4944e', + 'alias': 'first broker login', + 'description': 'browser based authentication', + 'providerId': 'basic-flow', + 'topLevel': True, + 'builtIn': False, + 'authenticationExecutions': [ + { + 'authenticator': 'auth-cookie', + 'requirement': 'ALTERNATIVE', + 'priority': 10, + 'userSetupAllowed': False, + 'autheticatorFlow': False + }, + ], + }] + return_value_executions_after = [ + { + 'id': 'b678e30c-8469-40a7-8c21-8d0cda76a591', + 'requirement': 'ALTERNATIVE', + 'displayName': 'Identity Provider Redirector', + 'requirementChoices': ['REQUIRED', 'DISABLED'], + 'configurable': True, + 'providerId': 'identity-provider-redirector', + 'level': 0, + 'index': 0 + }, + { + 'id': 'fdc208e9-c292-48b7-b7d1-1d98315ee893', + 'requirement': 'ALTERNATIVE', + 'displayName': 'Cookie', + 'requirementChoices': [ + 'REQUIRED', + 'ALTERNATIVE', + 'DISABLED' + ], + 'configurable': False, + 'providerId': 'auth-cookie', + 'level': 0, + 'index': 1 + }, + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before, copy_auth_flow=return_value_copied, + get_executions_representation=return_value_executions_after) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 1) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 2) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_auth_flow_from_copy_idempotency(self): + """Add an already existing authentication flow from copy of an other flow to test idempotency""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'copyFrom': 'first broker login', + 'authenticationExecutions': [ + { + 'providerId': 'identity-provider-redirector', + 'requirement': 'ALTERNATIVE', + }, + ], + 'state': 'present', + } + return_value_auth_flow_before = [{ + 'id': '71275d5e-e11f-4be4-b119-0abfa87987a4', + 'alias': 'Test create authentication flow copy', + 'description': '', + 'providerId': 'basic-flow', + 'topLevel': True, + 'builtIn': False, + 'authenticationExecutions': [ + { + 'authenticator': 'identity-provider-redirector', + 'requirement': 'ALTERNATIVE', + 'priority': 0, + 'userSetupAllowed': False, + 'autheticatorFlow': False + }, + { + 'authenticator': 'auth-cookie', + 'requirement': 'ALTERNATIVE', + 'priority': 0, + 'userSetupAllowed': False, + 'autheticatorFlow': False + }, + ], + }] + return_value_executions_after = [ + { + 'id': 'b678e30c-8469-40a7-8c21-8d0cda76a591', + 'requirement': 'ALTERNATIVE', + 'displayName': 'Identity Provider Redirector', + 'requirementChoices': ['REQUIRED', 'DISABLED'], + 'configurable': True, + 'providerId': 'identity-provider-redirector', + 'level': 0, + 'index': 0 + }, + { + 'id': 'fdc208e9-c292-48b7-b7d1-1d98315ee893', + 'requirement': 'ALTERNATIVE', + 'displayName': 'Cookie', + 'requirementChoices': [ + 'REQUIRED', + 'ALTERNATIVE', + 'DISABLED' + ], + 'configurable': False, + 'providerId': 'auth-cookie', + 'level': 0, + 'index': 1 + }, + ] + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before, + get_executions_representation=return_value_executions_after) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 2) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_auth_flow_without_copy(self): + """Add authentication without copy""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'authenticationExecutions': [ + { + 'providerId': 'identity-provider-redirector', + 'requirement': 'ALTERNATIVE', + 'authenticationConfig': { + 'alias': 'name', + 'config': { + 'defaultProvider': 'value' + }, + }, + }, + ], + 'state': 'present', + } + return_value_auth_flow_before = [{}] + return_value_created_empty_flow = [ + { + "alias": "Test of the keycloak_auth module", + "authenticationExecutions": [], + "builtIn": False, + "description": "", + "id": "513f5baa-cc42-47bf-b4b6-1d23ccc0a67f", + "providerId": "basic-flow", + "topLevel": True + }, + ] + return_value_executions_after = [ + { + 'id': 'b678e30c-8469-40a7-8c21-8d0cda76a591', + 'requirement': 'ALTERNATIVE', + 'displayName': 'Identity Provider Redirector', + 'requirementChoices': ['REQUIRED', 'DISABLED'], + 'configurable': True, + 'providerId': 'identity-provider-redirector', + 'level': 0, + 'index': 0 + }, + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before, + get_executions_representation=return_value_executions_after, create_empty_auth_flow=return_value_created_empty_flow) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 1) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 3) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_auth_flow_adding_exec(self): + """Update authentication flow by adding execution""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'authenticationExecutions': [ + { + 'providerId': 'identity-provider-redirector', + 'requirement': 'ALTERNATIVE', + 'authenticationConfig': { + 'alias': 'name', + 'config': { + 'defaultProvider': 'value' + }, + }, + }, + ], + 'state': 'present', + } + return_value_auth_flow_before = [{ + 'id': '71275d5e-e11f-4be4-b119-0abfa87987a4', + 'alias': 'Test create authentication flow copy', + 'description': '', + 'providerId': 'basic-flow', + 'topLevel': True, + 'builtIn': False, + 'authenticationExecutions': [ + { + 'authenticator': 'auth-cookie', + 'requirement': 'ALTERNATIVE', + 'priority': 0, + 'userSetupAllowed': False, + 'autheticatorFlow': False + }, + ], + }] + return_value_executions_after = [ + { + 'id': 'b678e30c-8469-40a7-8c21-8d0cda76a591', + 'requirement': 'DISABLED', + 'displayName': 'Identity Provider Redirector', + 'requirementChoices': ['REQUIRED', 'DISABLED'], + 'configurable': True, + 'providerId': 'identity-provider-redirector', + 'level': 0, + 'index': 0 + }, + { + 'id': 'fdc208e9-c292-48b7-b7d1-1d98315ee893', + 'requirement': 'ALTERNATIVE', + 'displayName': 'Cookie', + 'requirementChoices': [ + 'REQUIRED', + 'ALTERNATIVE', + 'DISABLED' + ], + 'configurable': False, + 'providerId': 'auth-cookie', + 'level': 0, + 'index': 1 + }, + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before, + get_executions_representation=return_value_executions_after) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 3) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_auth_flow(self): + """Delete authentication flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'state': 'absent', + } + return_value_auth_flow_before = [{ + 'id': '71275d5e-e11f-4be4-b119-0abfa87987a4', + 'alias': 'Test create authentication flow copy', + 'description': '', + 'providerId': 'basic-flow', + 'topLevel': True, + 'builtIn': False, + 'authenticationExecutions': [ + { + 'authenticator': 'auth-cookie', + 'requirement': 'ALTERNATIVE', + 'priority': 0, + 'userSetupAllowed': False, + 'autheticatorFlow': False + }, + ], + }] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 0) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_auth_flow_idempotency(self): + """Delete second time authentication flow to test idempotency""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'state': 'absent', + } + return_value_auth_flow_before = [{}] + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 0) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_force_update_auth_flow(self): + """Delete authentication flow and create new one""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'alias': 'Test create authentication flow copy', + 'authenticationExecutions': [ + { + 'providerId': 'identity-provider-redirector', + 'requirement': 'ALTERNATIVE', + 'authenticationConfig': { + 'alias': 'name', + 'config': { + 'defaultProvider': 'value' + }, + }, + }, + ], + 'state': 'present', + 'force': 'yes', + } + return_value_auth_flow_before = [{ + 'id': '71275d5e-e11f-4be4-b119-0abfa87987a4', + 'alias': 'Test create authentication flow copy', + 'description': '', + 'providerId': 'basic-flow', + 'topLevel': True, + 'builtIn': False, + 'authenticationExecutions': [ + { + 'authenticator': 'auth-cookie', + 'requirement': 'ALTERNATIVE', + 'priority': 0, + 'userSetupAllowed': False, + 'autheticatorFlow': False + }, + ], + }] + return_value_created_empty_flow = [ + { + "alias": "Test of the keycloak_auth module", + "authenticationExecutions": [], + "builtIn": False, + "description": "", + "id": "513f5baa-cc42-47bf-b4b6-1d23ccc0a67f", + "providerId": "basic-flow", + "topLevel": True + }, + ] + return_value_executions_after = [ + { + 'id': 'b678e30c-8469-40a7-8c21-8d0cda76a591', + 'requirement': 'DISABLED', + 'displayName': 'Identity Provider Redirector', + 'requirementChoices': ['REQUIRED', 'DISABLED'], + 'configurable': True, + 'providerId': 'identity-provider-redirector', + 'level': 0, + 'index': 0 + }, + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_authentication_flow_by_alias=return_value_auth_flow_before, + get_executions_representation=return_value_executions_after, create_empty_auth_flow=return_value_created_empty_flow) \ + as (mock_get_authentication_flow_by_alias, mock_copy_auth_flow, mock_create_empty_auth_flow, + mock_get_executions_representation, mock_delete_authentication_flow_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(len(mock_get_authentication_flow_by_alias.mock_calls), 1) + self.assertEqual(len(mock_copy_auth_flow.mock_calls), 0) + self.assertEqual(len(mock_create_empty_auth_flow.mock_calls), 1) + self.assertEqual(len(mock_get_executions_representation.mock_calls), 3) + self.assertEqual(len(mock_delete_authentication_flow_by_id.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_client.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client.py new file mode 100644 index 000000000..b44013af1 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client.py @@ -0,0 +1,150 @@ +# -*- 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_client + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_client_by_clientid=None, get_client_by_id=None, update_client=None, create_client=None, + delete_client=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_client.KeycloakAPI + with patch.object(obj, 'get_client_by_clientid', side_effect=get_client_by_clientid) as mock_get_client_by_clientid: + with patch.object(obj, 'get_client_by_id', side_effect=get_client_by_id) as mock_get_client_by_id: + with patch.object(obj, 'create_client', side_effect=create_client) as mock_create_client: + with patch.object(obj, 'update_client', side_effect=update_client) as mock_update_client: + with patch.object(obj, 'delete_client', side_effect=delete_client) as mock_delete_client: + yield mock_get_client_by_clientid, mock_get_client_by_id, mock_create_client, mock_update_client, mock_delete_client + + +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 TestKeycloakRealm(ModuleTestCase): + def setUp(self): + super(TestKeycloakRealm, self).setUp() + self.module = keycloak_client + + def test_authentication_flow_binding_overrides_feature(self): + """Add a new realm""" + + module_args = { + 'auth_keycloak_url': 'https: // auth.example.com / auth', + 'token': '{{ access_token }}', + 'state': 'present', + 'realm': 'master', + 'client_id': 'test', + 'authentication_flow_binding_overrides': { + 'browser': '4c90336b-bf1d-4b87-916d-3677ba4e5fbb' + } + } + return_value_get_client_by_clientid = [ + None, + { + "authenticationFlowBindingOverrides": { + "browser": "f9502b6d-d76a-4efe-8331-2ddd853c9f9c" + }, + "clientId": "onboardingid", + "enabled": "true", + "protocol": "openid-connect", + "redirectUris": [ + "*" + ] + } + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_client_by_clientid=return_value_get_client_by_clientid) \ + as (mock_get_client_by_clientid, mock_get_client_by_id, mock_create_client, mock_update_client, mock_delete_client): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(mock_get_client_by_clientid.call_count, 2) + self.assertEqual(mock_get_client_by_id.call_count, 0) + self.assertEqual(mock_create_client.call_count, 1) + self.assertEqual(mock_update_client.call_count, 0) + self.assertEqual(mock_delete_client.call_count, 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 new file mode 100644 index 000000000..58c8b9548 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py @@ -0,0 +1,573 @@ +# -*- 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_client_rolemapping + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_group_by_name=None, get_client_id=None, get_client_role_id_by_name=None, + get_client_group_rolemapping_by_id=None, get_client_group_available_rolemappings=None, + get_client_group_composite_rolemappings=None, add_group_rolemapping=None, + delete_group_rolemapping=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_client_rolemapping.KeycloakAPI + with patch.object(obj, 'get_group_by_name', + side_effect=get_group_by_name) as mock_get_group_by_name: + with patch.object(obj, 'get_client_id', + side_effect=get_client_id) as mock_get_client_id: + with patch.object(obj, 'get_client_role_id_by_name', + side_effect=get_client_role_id_by_name) as mock_get_client_role_id_by_name: + with patch.object(obj, 'get_client_group_rolemapping_by_id', + side_effect=get_client_group_rolemapping_by_id) as mock_get_client_group_rolemapping_by_id: + with patch.object(obj, 'get_client_group_available_rolemappings', + side_effect=get_client_group_available_rolemappings) as mock_get_client_group_available_rolemappings: + with patch.object(obj, 'get_client_group_composite_rolemappings', + side_effect=get_client_group_composite_rolemappings) as mock_get_client_group_composite_rolemappings: + with patch.object(obj, 'add_group_rolemapping', + side_effect=add_group_rolemapping) as mock_add_group_rolemapping: + with patch.object(obj, 'delete_group_rolemapping', + side_effect=delete_group_rolemapping) as mock_delete_group_rolemapping: + yield mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, \ + mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, \ + mock_get_client_group_composite_rolemappings, mock_delete_group_rolemapping + + +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 TestKeycloakRealm(ModuleTestCase): + def setUp(self): + super(TestKeycloakRealm, self).setUp() + self.module = keycloak_client_rolemapping + + def test_map_clientrole_to_group_with_name(self): + """Add a new realm""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_client_id': 'admin-cli', + 'realm': 'realm-name', + 'state': 'present', + 'client_id': 'test_client', + 'group_name': 'test_group', + 'roles': [ + { + 'name': 'test_role1', + }, + { + 'name': 'test_role1', + }, + ], + } + return_value_get_group_by_name = [{ + "access": { + "manage": "true", + "manageMembership": "true", + "view": "true" + }, + "attributes": "{}", + "clientRoles": "{}", + "id": "92f2400e-0ecb-4185-8950-12dcef616c2b", + "name": "test_group", + "path": "/test_group", + "realmRoles": "[]", + "subGroups": "[]" + }] + return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727" + return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe" + return_value_get_client_group_available_rolemappings = [[ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ]] + return_value_get_client_group_composite_rolemappings = [ + None, + [ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ] + ] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id, + get_client_role_id_by_name=return_value_get_client_role_id_by_name, + get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings, + get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \ + as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, + mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings, + mock_delete_group_rolemapping): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(mock_get_group_by_name.call_count, 1) + self.assertEqual(mock_get_client_id.call_count, 1) + self.assertEqual(mock_add_group_rolemapping.call_count, 1) + self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0) + self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1) + self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 2) + self.assertEqual(mock_delete_group_rolemapping.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_map_clientrole_to_group_with_name_idempotency(self): + """Add a new realm""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_client_id': 'admin-cli', + 'realm': 'realm-name', + 'state': 'present', + 'client_id': 'test_client', + 'group_name': 'test_group', + 'roles': [ + { + 'name': 'test_role1', + }, + { + 'name': 'test_role1', + }, + ], + } + return_value_get_group_by_name = [{ + "access": { + "manage": "true", + "manageMembership": "true", + "view": "true" + }, + "attributes": "{}", + "clientRoles": "{}", + "id": "92f2400e-0ecb-4185-8950-12dcef616c2b", + "name": "test_group", + "path": "/test_group", + "realmRoles": "[]", + "subGroups": "[]" + }] + return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727" + return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe" + return_value_get_client_group_available_rolemappings = [[]] + return_value_get_client_group_composite_rolemappings = [[ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ]] + + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id, + get_client_role_id_by_name=return_value_get_client_role_id_by_name, + get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings, + get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \ + as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, + mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings, + mock_delete_group_rolemapping): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(mock_get_group_by_name.call_count, 1) + self.assertEqual(mock_get_client_id.call_count, 1) + self.assertEqual(mock_add_group_rolemapping.call_count, 0) + self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0) + self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1) + self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 1) + self.assertEqual(mock_delete_group_rolemapping.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_map_clientrole_to_group_with_id(self): + """Add a new realm""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_client_id': 'admin-cli', + 'realm': 'realm-name', + 'state': 'present', + 'cid': 'c0f8490c-b224-4737-a567-20223e4c1727', + 'gid': '92f2400e-0ecb-4185-8950-12dcef616c2b', + 'roles': [ + { + 'name': 'test_role1', + }, + { + 'name': 'test_role1', + }, + ], + } + return_value_get_group_by_name = [{ + "access": { + "manage": "true", + "manageMembership": "true", + "view": "true" + }, + "attributes": "{}", + "clientRoles": "{}", + "id": "92f2400e-0ecb-4185-8950-12dcef616c2b", + "name": "test_group", + "path": "/test_group", + "realmRoles": "[]", + "subGroups": "[]" + }] + return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727" + return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe" + return_value_get_client_group_available_rolemappings = [[ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ]] + return_value_get_client_group_composite_rolemappings = [ + None, + [ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ] + ] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id, + get_client_role_id_by_name=return_value_get_client_role_id_by_name, + get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings, + get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \ + as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, + mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings, + mock_delete_group_rolemapping): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(mock_get_group_by_name.call_count, 0) + self.assertEqual(mock_get_client_id.call_count, 0) + self.assertEqual(mock_add_group_rolemapping.call_count, 1) + self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0) + self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1) + self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 2) + self.assertEqual(mock_delete_group_rolemapping.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_remove_clientrole_from_group(self): + """Add a new realm""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_client_id': 'admin-cli', + 'realm': 'realm-name', + 'state': 'absent', + 'client_id': 'test_client', + 'group_name': 'test_group', + 'roles': [ + { + 'name': 'test_role1', + }, + { + 'name': 'test_role1', + }, + ], + } + return_value_get_group_by_name = [{ + "access": { + "manage": "true", + "manageMembership": "true", + "view": "true" + }, + "attributes": "{}", + "clientRoles": "{}", + "id": "92f2400e-0ecb-4185-8950-12dcef616c2b", + "name": "test_group", + "path": "/test_group", + "realmRoles": "[]", + "subGroups": "[]" + }] + return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727" + return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe" + return_value_get_client_group_available_rolemappings = [[]] + return_value_get_client_group_composite_rolemappings = [ + [ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ], + [] + ] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id, + get_client_role_id_by_name=return_value_get_client_role_id_by_name, + get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings, + get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \ + as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, + mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings, + mock_delete_group_rolemapping): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(mock_get_group_by_name.call_count, 1) + self.assertEqual(mock_get_client_id.call_count, 1) + self.assertEqual(mock_add_group_rolemapping.call_count, 0) + self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0) + self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1) + self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 2) + self.assertEqual(mock_delete_group_rolemapping.call_count, 1) + + # Verify that the module's changed status matches what is expected + self.assertIs(exec_info.exception.args[0]['changed'], changed) + + def test_remove_clientrole_from_group_idempotency(self): + """Add a new realm""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_client_id': 'admin-cli', + 'realm': 'realm-name', + 'state': 'absent', + 'client_id': 'test_client', + 'group_name': 'test_group', + 'roles': [ + { + 'name': 'test_role1', + }, + { + 'name': 'test_role1', + }, + ], + } + return_value_get_group_by_name = [{ + "access": { + "manage": "true", + "manageMembership": "true", + "view": "true" + }, + "attributes": "{}", + "clientRoles": "{}", + "id": "92f2400e-0ecb-4185-8950-12dcef616c2b", + "name": "test_group", + "path": "/test_group", + "realmRoles": "[]", + "subGroups": "[]" + }] + return_value_get_client_id = "c0f8490c-b224-4737-a567-20223e4c1727" + return_value_get_client_role_id_by_name = "e91af074-cfd5-40ee-8ef5-ae0ae1ce69fe" + return_value_get_client_group_available_rolemappings = [ + [ + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "c2bf2edb-da94-4f2f-b9f2-196dfee3fe4d", + "name": "test_role2" + }, + { + "clientRole": "true", + "composite": "false", + "containerId": "c0f8490c-b224-4737-a567-20223e4c1727", + "id": "00a2d9a9-924e-49fa-8cde-c539c010ef6e", + "name": "test_role1" + } + ] + ] + return_value_get_client_group_composite_rolemappings = [[]] + + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_group_by_name=return_value_get_group_by_name, get_client_id=return_value_get_client_id, + get_client_role_id_by_name=return_value_get_client_role_id_by_name, + get_client_group_available_rolemappings=return_value_get_client_group_available_rolemappings, + get_client_group_composite_rolemappings=return_value_get_client_group_composite_rolemappings) \ + as (mock_get_group_by_name, mock_get_client_id, mock_get_client_role_id_by_name, mock_add_group_rolemapping, + mock_get_client_group_rolemapping_by_id, mock_get_client_group_available_rolemappings, mock_get_client_group_composite_rolemappings, + mock_delete_group_rolemapping): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(mock_get_group_by_name.call_count, 1) + self.assertEqual(mock_get_client_id.call_count, 1) + self.assertEqual(mock_add_group_rolemapping.call_count, 0) + self.assertEqual(mock_get_client_group_rolemapping_by_id.call_count, 0) + self.assertEqual(mock_get_client_group_available_rolemappings.call_count, 1) + self.assertEqual(mock_get_client_group_composite_rolemappings.call_count, 1) + self.assertEqual(mock_delete_group_rolemapping.call_count, 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_clientscope.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_clientscope.py new file mode 100644 index 000000000..ea015b05b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_clientscope.py @@ -0,0 +1,614 @@ +# -*- 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_clientscope + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_clientscope_by_name=None, get_clientscope_by_clientscopeid=None, create_clientscope=None, + update_clientscope=None, get_clientscope_protocolmapper_by_name=None, + update_clientscope_protocolmappers=None, create_clientscope_protocolmapper=None, + delete_clientscope=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): + ... + """ + + """ + get_clientscope_by_clientscopeid + delete_clientscope + """ + + obj = keycloak_clientscope.KeycloakAPI + with patch.object(obj, 'get_clientscope_by_name', side_effect=get_clientscope_by_name) \ + as mock_get_clientscope_by_name: + with patch.object(obj, 'get_clientscope_by_clientscopeid', side_effect=get_clientscope_by_clientscopeid) \ + as mock_get_clientscope_by_clientscopeid: + with patch.object(obj, 'create_clientscope', side_effect=create_clientscope) \ + as mock_create_clientscope: + with patch.object(obj, 'update_clientscope', return_value=update_clientscope) \ + as mock_update_clientscope: + with patch.object(obj, 'get_clientscope_protocolmapper_by_name', + side_effect=get_clientscope_protocolmapper_by_name) \ + as mock_get_clientscope_protocolmapper_by_name: + with patch.object(obj, 'update_clientscope_protocolmappers', + side_effect=update_clientscope_protocolmappers) \ + as mock_update_clientscope_protocolmappers: + with patch.object(obj, 'create_clientscope_protocolmapper', + side_effect=create_clientscope_protocolmapper) \ + as mock_create_clientscope_protocolmapper: + with patch.object(obj, 'delete_clientscope', side_effect=delete_clientscope) \ + as mock_delete_clientscope: + yield mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, \ + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, mock_update_clientscope_protocolmappers, \ + mock_create_clientscope_protocolmapper, mock_delete_clientscope + + +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_clientscope + + def test_create_clientscope(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'state': 'present', + 'name': 'my-new-kc-clientscope' + } + return_value_get_clientscope_by_name = [ + None, + { + "attributes": {}, + "id": "73fec1d2-f032-410c-8177-583104d01305", + "name": "my-new-kc-clientscope" + }] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_clientscope_by_name=return_value_get_clientscope_by_name) \ + as (mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, + mock_update_clientscope_protocolmappers, + mock_create_clientscope_protocolmapper, mock_delete_clientscope): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(mock_get_clientscope_by_name.call_count, 2) + self.assertEqual(mock_create_clientscope.call_count, 1) + self.assertEqual(mock_get_clientscope_by_clientscopeid.call_count, 0) + self.assertEqual(mock_update_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_protocolmapper_by_name.call_count, 0) + self.assertEqual(mock_update_clientscope_protocolmappers.call_count, 0) + self.assertEqual(mock_create_clientscope_protocolmapper.call_count, 0) + self.assertEqual(mock_delete_clientscope.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_create_clientscope_idempotency(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'state': 'present', + 'name': 'my-new-kc-clientscope' + } + return_value_get_clientscope_by_name = [{ + "attributes": {}, + "id": "73fec1d2-f032-410c-8177-583104d01305", + "name": "my-new-kc-clientscope" + }] + + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_clientscope_by_name=return_value_get_clientscope_by_name) \ + as (mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, + mock_update_clientscope_protocolmappers, + mock_create_clientscope_protocolmapper, mock_delete_clientscope): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(mock_get_clientscope_by_name.call_count, 1) + self.assertEqual(mock_create_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_by_clientscopeid.call_count, 0) + self.assertEqual(mock_update_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_protocolmapper_by_name.call_count, 0) + self.assertEqual(mock_update_clientscope_protocolmappers.call_count, 0) + self.assertEqual(mock_create_clientscope_protocolmapper.call_count, 0) + self.assertEqual(mock_delete_clientscope.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_clientscope(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'state': 'absent', + 'name': 'my-new-kc-clientscope' + } + return_value_get_clientscope_by_name = [{ + "attributes": {}, + "id": "73fec1d2-f032-410c-8177-583104d01305", + "name": "my-new-kc-clientscope" + }] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_clientscope_by_name=return_value_get_clientscope_by_name) \ + as (mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, + mock_update_clientscope_protocolmappers, + mock_create_clientscope_protocolmapper, mock_delete_clientscope): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(mock_get_clientscope_by_name.call_count, 1) + self.assertEqual(mock_create_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_by_clientscopeid.call_count, 0) + self.assertEqual(mock_update_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_protocolmapper_by_name.call_count, 0) + self.assertEqual(mock_update_clientscope_protocolmappers.call_count, 0) + self.assertEqual(mock_create_clientscope_protocolmapper.call_count, 0) + self.assertEqual(mock_delete_clientscope.call_count, 1) + + # Verify that the module's changed status matches what is expected + self.assertIs(exec_info.exception.args[0]['changed'], changed) + + def test_delete_clientscope_idempotency(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'state': 'absent', + 'name': 'my-new-kc-clientscope' + } + return_value_get_clientscope_by_name = [None] + + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_clientscope_by_name=return_value_get_clientscope_by_name) \ + as (mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, + mock_update_clientscope_protocolmappers, + mock_create_clientscope_protocolmapper, mock_delete_clientscope): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(mock_get_clientscope_by_name.call_count, 1) + self.assertEqual(mock_create_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_by_clientscopeid.call_count, 0) + self.assertEqual(mock_update_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_protocolmapper_by_name.call_count, 0) + self.assertEqual(mock_update_clientscope_protocolmappers.call_count, 0) + self.assertEqual(mock_create_clientscope_protocolmapper.call_count, 0) + self.assertEqual(mock_delete_clientscope.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_create_clientscope_with_protocolmappers(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'state': 'present', + 'name': 'my-new-kc-clientscope', + 'protocolMappers': [ + { + 'protocol': 'openid-connect', + 'config': { + 'full.path': 'true', + 'id.token.claim': 'true', + 'access.token.claim': 'true', + 'userinfo.token.claim': 'true', + 'claim.name': 'protocol1', + }, + 'name': 'protocol1', + 'protocolMapper': 'oidc-group-membership-mapper', + }, + { + 'protocol': 'openid-connect', + 'config': { + 'full.path': 'false', + 'id.token.claim': 'false', + 'access.token.claim': 'false', + 'userinfo.token.claim': 'false', + 'claim.name': 'protocol2', + }, + 'name': 'protocol2', + 'protocolMapper': 'oidc-group-membership-mapper', + }, + { + 'protocol': 'openid-connect', + 'config': { + 'full.path': 'true', + 'id.token.claim': 'false', + 'access.token.claim': 'true', + 'userinfo.token.claim': 'false', + 'claim.name': 'protocol3', + }, + 'name': 'protocol3', + 'protocolMapper': 'oidc-group-membership-mapper', + }, + ] + } + return_value_get_clientscope_by_name = [ + None, + { + "attributes": {}, + "id": "890ec72e-fe1d-4308-9f27-485ef7eaa182", + "name": "my-new-kc-clientscope", + "protocolMappers": [ + { + "config": { + "access.token.claim": "false", + "claim.name": "protocol2", + "full.path": "false", + "id.token.claim": "false", + "userinfo.token.claim": "false" + }, + "consentRequired": "false", + "id": "a7f19adb-cc58-41b1-94ce-782dc255139b", + "name": "protocol2", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "protocol3", + "full.path": "true", + "id.token.claim": "false", + "userinfo.token.claim": "false" + }, + "consentRequired": "false", + "id": "2103a559-185a-40f4-84ae-9ab311d5b812", + "name": "protocol3", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "protocol1", + "full.path": "true", + "id.token.claim": "true", + "userinfo.token.claim": "true" + }, + "consentRequired": "false", + "id": "bbf6390f-e95f-4c20-882b-9dad328363b9", + "name": "protocol1", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + }] + }] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_clientscope_by_name=return_value_get_clientscope_by_name) \ + as (mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, + mock_update_clientscope_protocolmappers, + mock_create_clientscope_protocolmapper, mock_delete_clientscope): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(mock_get_clientscope_by_name.call_count, 2) + self.assertEqual(mock_create_clientscope.call_count, 1) + self.assertEqual(mock_get_clientscope_by_clientscopeid.call_count, 0) + self.assertEqual(mock_update_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_protocolmapper_by_name.call_count, 0) + self.assertEqual(mock_update_clientscope_protocolmappers.call_count, 0) + self.assertEqual(mock_create_clientscope_protocolmapper.call_count, 0) + self.assertEqual(mock_delete_clientscope.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_clientscope_with_protocolmappers(self): + """Add a new authentication flow from copy of an other flow""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'auth_realm': 'master', + 'realm': 'realm-name', + 'state': 'present', + 'name': 'my-new-kc-clientscope', + 'protocolMappers': [ + { + 'protocol': 'openid-connect', + 'config': { + 'full.path': 'false', + 'id.token.claim': 'false', + 'access.token.claim': 'false', + 'userinfo.token.claim': 'false', + 'claim.name': 'protocol1_updated', + }, + 'name': 'protocol1', + 'protocolMapper': 'oidc-group-membership-mapper', + }, + { + 'protocol': 'openid-connect', + 'config': { + 'full.path': 'true', + 'id.token.claim': 'false', + 'access.token.claim': 'false', + 'userinfo.token.claim': 'false', + 'claim.name': 'protocol2_updated', + }, + 'name': 'protocol2', + 'protocolMapper': 'oidc-group-membership-mapper', + }, + { + 'protocol': 'openid-connect', + 'config': { + 'full.path': 'true', + 'id.token.claim': 'true', + 'access.token.claim': 'true', + 'userinfo.token.claim': 'true', + 'claim.name': 'protocol3_updated', + }, + 'name': 'protocol3', + 'protocolMapper': 'oidc-group-membership-mapper', + }, + ] + } + return_value_get_clientscope_by_name = [{ + "attributes": {}, + "id": "890ec72e-fe1d-4308-9f27-485ef7eaa182", + "name": "my-new-kc-clientscope", + "protocolMappers": [ + { + "config": { + "access.token.claim": "true", + "claim.name": "groups", + "full.path": "true", + "id.token.claim": "true", + "userinfo.token.claim": "true" + }, + "consentRequired": "false", + "id": "e077007a-367a-444f-91ef-70277a1d868d", + "name": "groups", + "protocol": "saml", + "protocolMapper": "oidc-group-membership-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "groups", + "full.path": "true", + "id.token.claim": "true", + "userinfo.token.claim": "true" + }, + "consentRequired": "false", + "id": "06c518aa-c627-43cc-9a82-d8467b508d34", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "groups", + "full.path": "true", + "id.token.claim": "true", + "userinfo.token.claim": "true" + }, + "consentRequired": "false", + "id": "1d03c557-d97e-40f4-ac35-6cecd74ea70d", + "name": "groups", + "protocol": "wsfed", + "protocolMapper": "oidc-group-membership-mapper" + } + ] + }] + return_value_get_clientscope_by_clientscopeid = [{ + "attributes": {}, + "id": "2286032f-451e-44d5-8be6-e45aac7983a1", + "name": "my-new-kc-clientscope", + "protocolMappers": [ + { + "config": { + "access.token.claim": "true", + "claim.name": "protocol1_updated", + "full.path": "true", + "id.token.claim": "false", + "userinfo.token.claim": "false" + }, + "consentRequired": "false", + "id": "a7f19adb-cc58-41b1-94ce-782dc255139b", + "name": "protocol2", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + }, + { + "config": { + "access.token.claim": "true", + "claim.name": "protocol1_updated", + "full.path": "true", + "id.token.claim": "false", + "userinfo.token.claim": "false" + }, + "consentRequired": "false", + "id": "2103a559-185a-40f4-84ae-9ab311d5b812", + "name": "protocol3", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + }, + { + "config": { + "access.token.claim": "false", + "claim.name": "protocol1_updated", + "full.path": "false", + "id.token.claim": "false", + "userinfo.token.claim": "false" + }, + "consentRequired": "false", + "id": "bbf6390f-e95f-4c20-882b-9dad328363b9", + "name": "protocol1", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper" + } + ] + }] + + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_clientscope_by_name=return_value_get_clientscope_by_name, + get_clientscope_by_clientscopeid=return_value_get_clientscope_by_clientscopeid) \ + as (mock_get_clientscope_by_name, mock_get_clientscope_by_clientscopeid, mock_create_clientscope, + mock_update_clientscope, mock_get_clientscope_protocolmapper_by_name, + mock_update_clientscope_protocolmappers, + mock_create_clientscope_protocolmapper, mock_delete_clientscope): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + # Verify number of call on each mock + self.assertEqual(mock_get_clientscope_by_name.call_count, 1) + self.assertEqual(mock_create_clientscope.call_count, 0) + self.assertEqual(mock_get_clientscope_by_clientscopeid.call_count, 1) + self.assertEqual(mock_update_clientscope.call_count, 1) + self.assertEqual(mock_get_clientscope_protocolmapper_by_name.call_count, 3) + self.assertEqual(mock_update_clientscope_protocolmappers.call_count, 3) + self.assertEqual(mock_create_clientscope_protocolmapper.call_count, 0) + self.assertEqual(mock_delete_clientscope.call_count, 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_identity_provider.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_identity_provider.py new file mode 100644 index 000000000..6fd258b8a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_identity_provider.py @@ -0,0 +1,588 @@ +# -*- 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_identity_provider + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_identity_provider, create_identity_provider=None, update_identity_provider=None, delete_identity_provider=None, + get_identity_provider_mappers=None, create_identity_provider_mapper=None, update_identity_provider_mapper=None, + delete_identity_provider_mapper=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_identity_provider.KeycloakAPI + with patch.object(obj, 'get_identity_provider', side_effect=get_identity_provider) \ + as mock_get_identity_provider: + with patch.object(obj, 'create_identity_provider', side_effect=create_identity_provider) \ + as mock_create_identity_provider: + with patch.object(obj, 'update_identity_provider', side_effect=update_identity_provider) \ + as mock_update_identity_provider: + with patch.object(obj, 'delete_identity_provider', side_effect=delete_identity_provider) \ + as mock_delete_identity_provider: + with patch.object(obj, 'get_identity_provider_mappers', side_effect=get_identity_provider_mappers) \ + as mock_get_identity_provider_mappers: + with patch.object(obj, 'create_identity_provider_mapper', side_effect=create_identity_provider_mapper) \ + as mock_create_identity_provider_mapper: + with patch.object(obj, 'update_identity_provider_mapper', side_effect=update_identity_provider_mapper) \ + as mock_update_identity_provider_mapper: + with patch.object(obj, 'delete_identity_provider_mapper', side_effect=delete_identity_provider_mapper) \ + as mock_delete_identity_provider_mapper: + yield mock_get_identity_provider, mock_create_identity_provider, mock_update_identity_provider, \ + mock_delete_identity_provider, mock_get_identity_provider_mappers, mock_create_identity_provider_mapper, \ + mock_update_identity_provider_mapper, mock_delete_identity_provider_mapper + + +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 TestKeycloakIdentityProvider(ModuleTestCase): + def setUp(self): + super(TestKeycloakIdentityProvider, self).setUp() + self.module = keycloak_identity_provider + + def test_create_when_absent(self): + """Add a new identity provider""" + + 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', + 'alias': 'oidc-idp', + 'display_name': 'OpenID Connect IdP', + 'enabled': True, + 'provider_id': 'oidc', + 'config': { + 'issuer': 'https://idp.example.com', + 'authorizationUrl': 'https://idp.example.com/auth', + 'tokenUrl': 'https://idp.example.com/token', + 'userInfoUrl': 'https://idp.example.com/userinfo', + 'clientAuthMethod': 'client_secret_post', + 'clientId': 'my-client', + 'clientSecret': 'secret', + 'syncMode': "FORCE", + }, + 'mappers': [{ + 'name': "first_name", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'config': { + 'claim': "first_name", + 'user.attribute': "first_name", + 'syncMode': "INHERIT", + } + }, { + 'name': "last_name", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'config': { + 'claim': "last_name", + 'user.attribute': "last_name", + 'syncMode': "INHERIT", + } + }] + } + return_value_idp_get = [ + None, + { + "addReadTokenRoleOnCreate": False, + "alias": "oidc-idp", + "authenticateByDefault": False, + "config": { + "authorizationUrl": "https://idp.example.com/auth", + "clientAuthMethod": "client_secret_post", + "clientId": "my-client", + "clientSecret": "no_log", + "issuer": "https://idp.example.com", + "syncMode": "FORCE", + "tokenUrl": "https://idp.example.com/token", + "userInfoUrl": "https://idp.example.com/userinfo" + }, + "displayName": "OpenID Connect IdP", + "enabled": True, + "firstBrokerLoginFlowAlias": "first broker login", + "internalId": "7ab437d5-f2bb-4ecc-91a8-315349454da6", + "linkOnly": False, + "providerId": "oidc", + "storeToken": False, + "trustEmail": False, + } + ] + return_value_mappers_get = [ + [{ + "config": { + "claim": "first_name", + "syncMode": "INHERIT", + "user.attribute": "first_name" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "first_name" + }, { + "config": { + "claim": "last_name", + "syncMode": "INHERIT", + "user.attribute": "last_name" + }, + "id": "f00c61e0-34d9-4bed-82d1-7e45acfefc09", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "last_name" + }] + ] + return_value_idp_created = [None] + return_value_mapper_created = [None, None] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_identity_provider=return_value_idp_get, get_identity_provider_mappers=return_value_mappers_get, + create_identity_provider=return_value_idp_created, create_identity_provider_mapper=return_value_mapper_created) \ + as (mock_get_identity_provider, mock_create_identity_provider, mock_update_identity_provider, mock_delete_identity_provider, + mock_get_identity_provider_mappers, mock_create_identity_provider_mapper, mock_update_identity_provider_mapper, + mock_delete_identity_provider_mapper): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_identity_provider.mock_calls), 2) + self.assertEqual(len(mock_get_identity_provider_mappers.mock_calls), 1) + self.assertEqual(len(mock_create_identity_provider.mock_calls), 1) + self.assertEqual(len(mock_create_identity_provider_mapper.mock_calls), 2) + + # Verify that the module's changed status matches what is expected + self.assertIs(exec_info.exception.args[0]['changed'], changed) + + def test_update_when_present(self): + """Update existing identity provider""" + + 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', + 'alias': 'oidc-idp', + 'display_name': 'OpenID Connect IdP', + 'enabled': True, + 'provider_id': 'oidc', + 'config': { + 'issuer': 'https://idp.example.com', + 'authorizationUrl': 'https://idp.example.com/auth', + 'tokenUrl': 'https://idp.example.com/token', + 'userInfoUrl': 'https://idp.example.com/userinfo', + 'clientAuthMethod': 'client_secret_post', + 'clientId': 'my-client', + 'clientSecret': 'secret', + 'syncMode': "FORCE" + }, + 'mappers': [{ + 'name': "username", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'config': { + 'claim': "username", + 'user.attribute': "username", + 'syncMode': "INHERIT", + } + }, { + 'name': "first_name", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'config': { + 'claim': "first_name", + 'user.attribute': "first_name", + 'syncMode': "INHERIT", + } + }, { + 'name': "last_name", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'config': { + 'claim': "last_name", + 'user.attribute': "last_name", + 'syncMode': "INHERIT", + } + }] + } + return_value_idp_get = [ + { + "addReadTokenRoleOnCreate": False, + "alias": "oidc-idp", + "authenticateByDefault": False, + "config": { + "authorizationUrl": "https://idp.example.com/auth", + "clientAuthMethod": "client_secret_post", + "clientId": "my-client", + "clientSecret": "no_log", + "issuer": "https://idp.example.com", + "syncMode": "FORCE", + "tokenUrl": "https://idp.example.com/token", + "userInfoUrl": "https://idp.example.com/userinfo" + }, + "displayName": "OpenID Connect IdP changeme", + "enabled": True, + "firstBrokerLoginFlowAlias": "first broker login", + "internalId": "7ab437d5-f2bb-4ecc-91a8-315349454da6", + "linkOnly": False, + "providerId": "oidc", + "storeToken": False, + "trustEmail": False, + }, + { + "addReadTokenRoleOnCreate": False, + "alias": "oidc-idp", + "authenticateByDefault": False, + "config": { + "authorizationUrl": "https://idp.example.com/auth", + "clientAuthMethod": "client_secret_post", + "clientId": "my-client", + "clientSecret": "no_log", + "issuer": "https://idp.example.com", + "syncMode": "FORCE", + "tokenUrl": "https://idp.example.com/token", + "userInfoUrl": "https://idp.example.com/userinfo" + }, + "displayName": "OpenID Connect IdP", + "enabled": True, + "firstBrokerLoginFlowAlias": "first broker login", + "internalId": "7ab437d5-f2bb-4ecc-91a8-315349454da6", + "linkOnly": False, + "providerId": "oidc", + "storeToken": False, + "trustEmail": False, + } + ] + return_value_mappers_get = [ + [{ + 'config': { + 'claim': "username", + 'syncMode': "INHERIT", + 'user.attribute': "username" + }, + "id": "616f11ba-b9ae-42ae-bd1b-bc618741c10b", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'name': "username" + }, { + "config": { + "claim": "first_name_changeme", + "syncMode": "INHERIT", + "user.attribute": "first_name" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "first_name" + }], + [{ + 'config': { + 'claim': "username", + 'syncMode': "INHERIT", + 'user.attribute': "username" + }, + "id": "616f11ba-b9ae-42ae-bd1b-bc618741c10b", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'name': "username" + }, { + "config": { + "claim": "first_name_changeme", + "syncMode": "INHERIT", + "user.attribute": "first_name" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "first_name" + }], + [{ + 'config': { + 'claim': "username", + 'syncMode': "INHERIT", + 'user.attribute': "username" + }, + "id": "616f11ba-b9ae-42ae-bd1b-bc618741c10b", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'name': "username" + }, { + "config": { + "claim": "first_name_changeme", + "syncMode": "INHERIT", + "user.attribute": "first_name" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "first_name" + }], + [{ + 'config': { + 'claim': "username", + 'syncMode': "INHERIT", + 'user.attribute': "username" + }, + "id": "616f11ba-b9ae-42ae-bd1b-bc618741c10b", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'name': "username" + }, { + "config": { + "claim": "first_name_changeme", + "syncMode": "INHERIT", + "user.attribute": "first_name" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "first_name" + }], + [{ + 'config': { + 'claim': "username", + 'syncMode': "INHERIT", + 'user.attribute': "username" + }, + "id": "616f11ba-b9ae-42ae-bd1b-bc618741c10b", + 'identityProviderAlias': "oidc-idp", + 'identityProviderMapper': "oidc-user-attribute-idp-mapper", + 'name': "username" + }, { + "config": { + "claim": "first_name", + "syncMode": "INHERIT", + "user.attribute": "first_name" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "first_name" + }, { + "config": { + "claim": "last_name", + "syncMode": "INHERIT", + "user.attribute": "last_name" + }, + "id": "f00c61e0-34d9-4bed-82d1-7e45acfefc09", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "last_name" + }] + ] + return_value_idp_updated = [None] + return_value_mapper_updated = [None] + return_value_mapper_created = [None] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_identity_provider=return_value_idp_get, get_identity_provider_mappers=return_value_mappers_get, + update_identity_provider=return_value_idp_updated, update_identity_provider_mapper=return_value_mapper_updated, + create_identity_provider_mapper=return_value_mapper_created) \ + as (mock_get_identity_provider, mock_create_identity_provider, mock_update_identity_provider, mock_delete_identity_provider, + mock_get_identity_provider_mappers, mock_create_identity_provider_mapper, mock_update_identity_provider_mapper, + mock_delete_identity_provider_mapper): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_identity_provider.mock_calls), 2) + self.assertEqual(len(mock_get_identity_provider_mappers.mock_calls), 5) + self.assertEqual(len(mock_update_identity_provider.mock_calls), 1) + self.assertEqual(len(mock_update_identity_provider_mapper.mock_calls), 1) + self.assertEqual(len(mock_create_identity_provider_mapper.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 identity provider""" + + 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', + 'alias': 'oidc-idp', + 'state': 'absent', + } + return_value_idp_get = [None] + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_identity_provider=return_value_idp_get) \ + as (mock_get_identity_provider, mock_create_identity_provider, mock_update_identity_provider, mock_delete_identity_provider, + mock_get_identity_provider_mappers, mock_create_identity_provider_mapper, mock_update_identity_provider_mapper, + mock_delete_identity_provider_mapper): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_identity_provider.mock_calls), 1) + self.assertEqual(len(mock_delete_identity_provider.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_when_present(self): + """Remove an existing identity provider""" + + 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', + 'alias': 'oidc-idp', + 'state': 'absent', + } + return_value_idp_get = [ + { + "addReadTokenRoleOnCreate": False, + "alias": "oidc-idp", + "authenticateByDefault": False, + "config": { + "authorizationUrl": "https://idp.example.com/auth", + "clientAuthMethod": "client_secret_post", + "clientId": "my-client", + "clientSecret": "no_log", + "issuer": "https://idp.example.com", + "syncMode": "FORCE", + "tokenUrl": "https://idp.example.com/token", + "userInfoUrl": "https://idp.example.com/userinfo" + }, + "displayName": "OpenID Connect IdP", + "enabled": True, + "firstBrokerLoginFlowAlias": "first broker login", + "internalId": "7ab437d5-f2bb-4ecc-91a8-315349454da6", + "linkOnly": False, + "providerId": "oidc", + "storeToken": False, + "trustEmail": False, + }, + None + ] + return_value_mappers_get = [ + [{ + "config": { + "claim": "email", + "syncMode": "INHERIT", + "user.attribute": "email" + }, + "id": "5fde49bb-93bd-4f5d-97d6-c5d0c1d07aef", + "identityProviderAlias": "oidc-idp", + "identityProviderMapper": "oidc-user-attribute-idp-mapper", + "name": "email" + }] + ] + return_value_idp_deleted = [None] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_identity_provider=return_value_idp_get, get_identity_provider_mappers=return_value_mappers_get, + delete_identity_provider=return_value_idp_deleted) \ + as (mock_get_identity_provider, mock_create_identity_provider, mock_update_identity_provider, mock_delete_identity_provider, + mock_get_identity_provider_mappers, mock_create_identity_provider_mapper, mock_update_identity_provider_mapper, + mock_delete_identity_provider_mapper): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_identity_provider.mock_calls), 1) + self.assertEqual(len(mock_get_identity_provider_mappers.mock_calls), 1) + self.assertEqual(len(mock_delete_identity_provider.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_realm.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_realm.py new file mode 100644 index 000000000..72993cbdf --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_realm.py @@ -0,0 +1,311 @@ +# -*- 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_realm + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_realm_by_id, create_realm=None, update_realm=None, delete_realm=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_realm.KeycloakAPI + with patch.object(obj, 'get_realm_by_id', side_effect=get_realm_by_id) as mock_get_realm_by_id: + with patch.object(obj, 'create_realm', side_effect=create_realm) as mock_create_realm: + with patch.object(obj, 'update_realm', side_effect=update_realm) as mock_update_realm: + with patch.object(obj, 'delete_realm', side_effect=delete_realm) as mock_delete_realm: + yield mock_get_realm_by_id, mock_create_realm, mock_update_realm, mock_delete_realm + + +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 TestKeycloakRealm(ModuleTestCase): + def setUp(self): + super(TestKeycloakRealm, self).setUp() + self.module = keycloak_realm + + def test_create_when_absent(self): + """Add a new realm""" + + 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, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + } + return_value_absent = [None, {'id': 'realm-name', 'realm': 'realm-name', 'enabled': True}] + return_value_created = [{ + 'code': 201, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + }] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_realm_by_id=return_value_absent, create_realm=return_value_created) \ + as (mock_get_realm_by_id, mock_create_realm, mock_update_realm, mock_delete_realm): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_by_id.mock_calls), 2) + self.assertEqual(len(mock_create_realm.mock_calls), 1) + self.assertEqual(len(mock_update_realm.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_when_present_with_change(self): + """Update with change a realm""" + + 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, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': False + } + return_value_absent = [ + { + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + }, + { + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': False + } + ] + return_value_updated = [{ + 'code': 201, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': False + }] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_realm_by_id=return_value_absent, update_realm=return_value_updated) \ + as (mock_get_realm_by_id, mock_create_realm, mock_update_realm, mock_delete_realm): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_by_id.mock_calls), 2) + self.assertEqual(len(mock_create_realm.mock_calls), 0) + self.assertEqual(len(mock_update_realm.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_create_when_present_no_change(self): + """Update without change a realm""" + + 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, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + } + return_value_absent = [ + { + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + }, + { + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + } + ] + return_value_updated = [{ + 'code': 201, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True + }] + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_realm_by_id=return_value_absent, update_realm=return_value_updated) \ + as (mock_get_realm_by_id, mock_create_realm, mock_update_realm, mock_delete_realm): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_by_id.mock_calls), 2) + self.assertEqual(len(mock_create_realm.mock_calls), 0) + self.assertEqual(len(mock_update_realm.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""" + + 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, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True, + 'state': 'absent' + } + return_value_absent = [None] + return_value_deleted = [None] + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_realm_by_id=return_value_absent, delete_realm=return_value_deleted) \ + as (mock_get_realm_by_id, mock_create_realm, mock_update_realm, mock_delete_realm): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_by_id.mock_calls), 1) + self.assertEqual(len(mock_delete_realm.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_when_present(self): + """Remove a present realm""" + + 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, + 'id': 'realm-name', + 'realm': 'realm-name', + 'enabled': True, + 'state': 'absent' + } + return_value_absent = [ + { + 'id': 'realm-name', + 'realm': 'realm-name' + }] + return_value_deleted = [None] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_realm_by_id=return_value_absent, delete_realm=return_value_deleted) \ + as (mock_get_realm_by_id, mock_create_realm, mock_update_realm, mock_delete_realm): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_by_id.mock_calls), 1) + self.assertEqual(len(mock_delete_realm.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_realm_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_realm_info.py new file mode 100644 index 000000000..41095a878 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_realm_info.py @@ -0,0 +1,122 @@ +# -*- 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_realm_info + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_realm_info_by_id): + """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_realm_info.KeycloakAPI + with patch.object(obj, 'get_realm_info_by_id', side_effect=get_realm_info_by_id) as mock_get_realm_info_by_id: + yield mock_get_realm_info_by_id + + +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 TestKeycloakRealmRole(ModuleTestCase): + def setUp(self): + super(TestKeycloakRealmRole, self).setUp() + self.module = keycloak_realm_info + + def test_get_public_info(self): + """Get realm public info""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'realm': 'my-realm', + } + return_value = [ + None, + { + "realm": "my-realm", + "public_key": "MIIBIjANBgkqhkiG9w0BAQEF...", + "token-service": "https://auth.mock.com/auth/realms/my-realm/protocol/openid-connect", + "account-service": "https://auth.mock.com/auth/realms/my-realm/account", + "tokens-not-before": 0, + } + ] + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_realm_info_by_id=return_value) \ + as (mock_get_realm_info_by_id): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_info_by_id.mock_calls), 1) + + +if __name__ == '__main__': + unittest.main() 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 new file mode 100644 index 000000000..c48c9771a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py @@ -0,0 +1,327 @@ +# -*- 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_role + +from itertools import count + +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): + """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_role.KeycloakAPI + with patch.object(obj, 'get_realm_role', side_effect=get_realm_role) as mock_get_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 + + +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 TestKeycloakRealmRole(ModuleTestCase): + def setUp(self): + super(TestKeycloakRealmRole, self).setUp() + self.module = keycloak_role + + def test_create_when_absent(self): + """Add a new 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', + } + return_value_absent = [ + None, + { + "attributes": {}, + "clientRole": False, + "composite": False, + "containerId": "realm-name", + "description": "role-description", + "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966", + "name": "role-name", + } + ] + return_value_created = [None] + changed = True + + set_module_args(module_args) + + # Run the module + + 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): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_role.mock_calls), 2) + self.assertEqual(len(mock_create_realm_role.mock_calls), 1) + self.assertEqual(len(mock_update_realm_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_when_present_with_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', + 'name': 'role-name', + 'description': 'new-role-description', + } + return_value_present = [ + { + "attributes": {}, + "clientRole": False, + "composite": False, + "containerId": "realm-name", + "description": "role-description", + "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966", + "name": "role-name", + }, + { + "attributes": {}, + "clientRole": False, + "composite": False, + "containerId": "realm-name", + "description": "new-role-description", + "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966", + "name": "role-name", + } + ] + return_value_updated = [None] + changed = True + + 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) \ + as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_role.mock_calls), 2) + self.assertEqual(len(mock_create_realm_role.mock_calls), 0) + self.assertEqual(len(mock_update_realm_role.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_create_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', + } + return_value_present = [ + { + "attributes": {}, + "clientRole": False, + "composite": False, + "containerId": "realm-name", + "description": "role-description", + "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966", + "name": "role-name", + }, + { + "attributes": {}, + "clientRole": False, + "composite": False, + "containerId": "realm-name", + "description": "role-description", + "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966", + "name": "role-name", + } + ] + return_value_updated = [None] + 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) \ + as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role): + 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) + + # 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""" + + 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', + 'state': 'absent' + } + return_value_absent = [None] + return_value_deleted = [None] + changed = False + + set_module_args(module_args) + + # Run the module + + 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): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_role.mock_calls), 1) + self.assertEqual(len(mock_delete_realm_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_delete_when_present(self): + """Remove a present 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', + 'state': 'absent' + } + return_value_absent = [ + { + "attributes": {}, + "clientRole": False, + "composite": False, + "containerId": "realm-name", + "description": "role-description", + "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966", + "name": "role-name", + } + ] + return_value_deleted = [None] + changed = True + + set_module_args(module_args) + + # Run the module + + 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): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_realm_role.mock_calls), 1) + self.assertEqual(len(mock_delete_realm_role.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_federation.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py new file mode 100644 index 000000000..8d3dcaa23 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py @@ -0,0 +1,582 @@ +# -*- 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_federation + +from itertools import count + +from ansible.module_utils.six import StringIO + + +@contextmanager +def patch_keycloak_api(get_components=None, get_component=None, create_component=None, update_component=None, delete_component=None): + """Mock context manager for patching the methods in KeycloakAPI + """ + + obj = keycloak_user_federation.KeycloakAPI + with patch.object(obj, 'get_components', side_effect=get_components) \ + as mock_get_components: + with patch.object(obj, 'get_component', side_effect=get_component) \ + as mock_get_component: + with patch.object(obj, 'create_component', side_effect=create_component) \ + as mock_create_component: + with patch.object(obj, 'update_component', side_effect=update_component) \ + as mock_update_component: + with patch.object(obj, 'delete_component', side_effect=delete_component) \ + as mock_delete_component: + yield mock_get_components, mock_get_component, mock_create_component, mock_update_component, mock_delete_component + + +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 TestKeycloakUserFederation(ModuleTestCase): + def setUp(self): + super(TestKeycloakUserFederation, self).setUp() + self.module = keycloak_user_federation + + def test_create_when_absent(self): + """Add a new user federation""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'realm': 'realm-name', + 'name': 'kerberos', + 'state': 'present', + 'provider_id': 'kerberos', + 'provider_type': 'org.keycloak.storage.UserStorageProvider', + 'config': { + 'priority': 0, + 'enabled': True, + 'cachePolicy': 'DEFAULT', + 'kerberosRealm': 'REALM', + 'serverPrincipal': 'princ', + 'keyTab': 'keytab', + 'allowPasswordAuthentication': False, + 'updateProfileFirstLogin': False, + }, + } + return_value_component_create = [ + { + "id": "ebb7d999-60cc-4dfe-ab79-48f7bbd9d4d9", + "name": "kerberos", + "providerId": "kerberos", + "providerType": "org.keycloak.storage.UserStorageProvider", + "parentId": "kerberos", + "config": { + "serverPrincipal": [ + "princ" + ], + "allowPasswordAuthentication": [ + "false" + ], + "keyTab": [ + "keytab" + ], + "cachePolicy": [ + "DEFAULT" + ], + "updateProfileFirstLogin": [ + "false" + ], + "kerberosRealm": [ + "REALM" + ], + "priority": [ + "0" + ], + "enabled": [ + "true" + ] + } + } + ] + return_value_components_get = [ + [], [] + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_components=return_value_components_get, create_component=return_value_component_create) \ + as (mock_get_components, mock_get_component, mock_create_component, mock_update_component, mock_delete_component): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_components.mock_calls), 1) + self.assertEqual(len(mock_get_component.mock_calls), 0) + self.assertEqual(len(mock_create_component.mock_calls), 1) + self.assertEqual(len(mock_update_component.mock_calls), 0) + self.assertEqual(len(mock_delete_component.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_when_present(self): + """Update existing user federation""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'realm': 'realm-name', + 'name': 'kerberos', + 'state': 'present', + 'provider_id': 'kerberos', + 'provider_type': 'org.keycloak.storage.UserStorageProvider', + 'config': { + 'priority': 0, + 'enabled': True, + 'cachePolicy': 'DEFAULT', + 'kerberosRealm': 'REALM', + 'serverPrincipal': 'princ', + 'keyTab': 'keytab', + 'allowPasswordAuthentication': False, + 'updateProfileFirstLogin': False, + }, + } + return_value_components_get = [ + [ + { + "id": "ebb7d999-60cc-4dfe-ab79-48f7bbd9d4d9", + "name": "kerberos", + "providerId": "kerberos", + "providerType": "org.keycloak.storage.UserStorageProvider", + "parentId": "kerberos", + "config": { + "serverPrincipal": [ + "princ" + ], + "allowPasswordAuthentication": [ + "false" + ], + "keyTab": [ + "keytab" + ], + "cachePolicy": [ + "DEFAULT" + ], + "updateProfileFirstLogin": [ + "false" + ], + "kerberosRealm": [ + "REALM" + ], + "priority": [ + "0" + ], + "enabled": [ + "false" + ] + } + } + ], + [] + ] + return_value_component_get = [ + { + "id": "ebb7d999-60cc-4dfe-ab79-48f7bbd9d4d9", + "name": "kerberos", + "providerId": "kerberos", + "providerType": "org.keycloak.storage.UserStorageProvider", + "parentId": "kerberos", + "config": { + "serverPrincipal": [ + "princ" + ], + "allowPasswordAuthentication": [ + "false" + ], + "keyTab": [ + "keytab" + ], + "cachePolicy": [ + "DEFAULT" + ], + "updateProfileFirstLogin": [ + "false" + ], + "kerberosRealm": [ + "REALM" + ], + "priority": [ + "0" + ], + "enabled": [ + "true" + ] + } + } + ] + return_value_component_update = [ + None + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_components=return_value_components_get, get_component=return_value_component_get, + update_component=return_value_component_update) \ + as (mock_get_components, mock_get_component, mock_create_component, mock_update_component, mock_delete_component): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_components.mock_calls), 2) + self.assertEqual(len(mock_get_component.mock_calls), 1) + self.assertEqual(len(mock_create_component.mock_calls), 0) + self.assertEqual(len(mock_update_component.mock_calls), 1) + self.assertEqual(len(mock_delete_component.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_with_mappers(self): + """Add a new user federation with mappers""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'realm': 'realm-name', + 'name': 'ldap', + 'state': 'present', + 'provider_id': 'ldap', + 'provider_type': 'org.keycloak.storage.UserStorageProvider', + 'config': { + 'priority': 0, + 'enabled': True, + 'cachePolicy': 'DEFAULT', + 'batchSizeForSync': 1000, + 'editMode': 'READ_ONLY', + 'importEnabled': True, + 'syncRegistrations': False, + 'vendor': 'other', + 'usernameLDAPAttribute': 'uid', + 'rdnLDAPAttribute': 'uid', + 'uuidLDAPAttribute': 'entryUUID', + 'userObjectClasses': 'inetOrgPerson, organizationalPerson', + 'connectionUrl': 'ldaps://ldap.example.com:636', + 'usersDn': 'ou=Users,dc=example,dc=com', + 'authType': 'none', + 'searchScope': 1, + 'validatePasswordPolicy': False, + 'trustEmail': False, + 'useTruststoreSpi': 'ldapsOnly', + 'connectionPooling': True, + 'pagination': True, + 'allowKerberosAuthentication': False, + 'debug': False, + 'useKerberosForPasswordAuthentication': False, + }, + 'mappers': [ + { + 'name': 'full name', + 'providerId': 'full-name-ldap-mapper', + 'providerType': 'org.keycloak.storage.ldap.mappers.LDAPStorageMapper', + 'config': { + 'ldap.full.name.attribute': 'cn', + 'read.only': True, + 'write.only': False, + } + } + ] + } + return_value_components_get = [ + [], [] + ] + return_value_component_create = [ + { + "id": "eb691537-b73c-4cd8-b481-6031c26499d8", + "name": "ldap", + "providerId": "ldap", + "providerType": "org.keycloak.storage.UserStorageProvider", + "parentId": "ldap", + "config": { + "pagination": [ + "true" + ], + "connectionPooling": [ + "true" + ], + "usersDn": [ + "ou=Users,dc=example,dc=com" + ], + "cachePolicy": [ + "DEFAULT" + ], + "useKerberosForPasswordAuthentication": [ + "false" + ], + "importEnabled": [ + "true" + ], + "enabled": [ + "true" + ], + "usernameLDAPAttribute": [ + "uid" + ], + "vendor": [ + "other" + ], + "uuidLDAPAttribute": [ + "entryUUID" + ], + "connectionUrl": [ + "ldaps://ldap.example.com:636" + ], + "allowKerberosAuthentication": [ + "false" + ], + "syncRegistrations": [ + "false" + ], + "authType": [ + "none" + ], + "debug": [ + "false" + ], + "searchScope": [ + "1" + ], + "useTruststoreSpi": [ + "ldapsOnly" + ], + "trustEmail": [ + "false" + ], + "priority": [ + "0" + ], + "userObjectClasses": [ + "inetOrgPerson, organizationalPerson" + ], + "rdnLDAPAttribute": [ + "uid" + ], + "editMode": [ + "READ_ONLY" + ], + "validatePasswordPolicy": [ + "false" + ], + "batchSizeForSync": [ + "1000" + ] + } + }, + { + "id": "2dfadafd-8b34-495f-a98b-153e71a22311", + "name": "full name", + "providerId": "full-name-ldap-mapper", + "providerType": "org.keycloak.storage.ldap.mappers.LDAPStorageMapper", + "parentId": "eb691537-b73c-4cd8-b481-6031c26499d8", + "config": { + "ldap.full.name.attribute": [ + "cn" + ], + "read.only": [ + "true" + ], + "write.only": [ + "false" + ] + } + } + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_components=return_value_components_get, create_component=return_value_component_create) \ + as (mock_get_components, mock_get_component, mock_create_component, mock_update_component, mock_delete_component): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_components.mock_calls), 2) + self.assertEqual(len(mock_get_component.mock_calls), 0) + self.assertEqual(len(mock_create_component.mock_calls), 2) + self.assertEqual(len(mock_update_component.mock_calls), 0) + self.assertEqual(len(mock_delete_component.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_when_absent(self): + """Remove an absent user federation""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'realm': 'realm-name', + 'name': 'kerberos', + 'state': 'absent', + } + return_value_components_get = [ + [] + ] + changed = False + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_components=return_value_components_get) \ + as (mock_get_components, mock_get_component, mock_create_component, mock_update_component, mock_delete_component): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_components.mock_calls), 1) + self.assertEqual(len(mock_get_component.mock_calls), 0) + self.assertEqual(len(mock_create_component.mock_calls), 0) + self.assertEqual(len(mock_update_component.mock_calls), 0) + self.assertEqual(len(mock_delete_component.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_when_present(self): + """Remove an existing user federation""" + + module_args = { + 'auth_keycloak_url': 'http://keycloak.url/auth', + 'auth_realm': 'master', + 'auth_username': 'admin', + 'auth_password': 'admin', + 'realm': 'realm-name', + 'name': 'kerberos', + 'state': 'absent', + } + return_value_components_get = [ + [ + { + "id": "ebb7d999-60cc-4dfe-ab79-48f7bbd9d4d9", + "name": "kerberos", + "providerId": "kerberos", + "providerType": "org.keycloak.storage.UserStorageProvider", + "parentId": "kerberos", + "config": { + "serverPrincipal": [ + "princ" + ], + "allowPasswordAuthentication": [ + "false" + ], + "keyTab": [ + "keytab" + ], + "cachePolicy": [ + "DEFAULT" + ], + "updateProfileFirstLogin": [ + "false" + ], + "kerberosRealm": [ + "REALM" + ], + "priority": [ + "0" + ], + "enabled": [ + "false" + ] + } + } + ], + [] + ] + return_value_component_delete = [ + None + ] + changed = True + + set_module_args(module_args) + + # Run the module + + with mock_good_connection(): + with patch_keycloak_api(get_components=return_value_components_get, delete_component=return_value_component_delete) \ + as (mock_get_components, mock_get_component, mock_create_component, mock_update_component, mock_delete_component): + with self.assertRaises(AnsibleExitJson) as exec_info: + self.module.main() + + self.assertEqual(len(mock_get_components.mock_calls), 2) + self.assertEqual(len(mock_get_component.mock_calls), 0) + self.assertEqual(len(mock_create_component.mock_calls), 0) + self.assertEqual(len(mock_update_component.mock_calls), 0) + self.assertEqual(len(mock_delete_component.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_linode.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_linode.py new file mode 100644 index 000000000..9e7b158d8 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_linode.py @@ -0,0 +1,22 @@ +# 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 pytest + +from ansible_collections.community.general.plugins.modules import linode +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + +from .linode_conftest import api_key, auth # noqa: F401, pylint: disable=unused-import + +if not linode.HAS_LINODE: + pytestmark = pytest.mark.skip('test_linode.py requires the `linode-python` module') + + +def test_name_is_a_required_parameter(api_key, auth): + with pytest.raises(SystemExit): + set_module_args({}) + linode.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_linode_v4.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_linode_v4.py new file mode 100644 index 000000000..915a82f08 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_linode_v4.py @@ -0,0 +1,379 @@ +# 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 +import os +import sys + +import pytest + +linode_apiv4 = pytest.importorskip('linode_api4') +mandatory_py_version = pytest.mark.skipif( + sys.version_info < (2, 7), + reason='The linode_api4 dependency requires python2.7 or higher' +) + +from linode_api4.errors import ApiError as LinodeApiError +from linode_api4 import LinodeClient + +from ansible_collections.community.general.plugins.modules import linode_v4 +from ansible_collections.community.general.plugins.module_utils.linode import get_user_agent +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args +from ansible_collections.community.general.tests.unit.compat import mock + +from .linode_conftest import access_token, no_access_token_in_env, default_args, mock_linode # noqa: F401, pylint: disable=unused-import + + +def test_mandatory_state_is_validated(capfd): + with pytest.raises(SystemExit): + set_module_args({'label': 'foo'}) + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert all(txt in results['msg'] for txt in ('state', 'required')) + assert results['failed'] is True + + +def test_mandatory_label_is_validated(capfd): + with pytest.raises(SystemExit): + set_module_args({'state': 'present'}) + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert all(txt in results['msg'] for txt in ('label', 'required')) + assert results['failed'] is True + + +def test_mandatory_access_token_is_validated(default_args, + no_access_token_in_env, + capfd): + with pytest.raises(SystemExit): + set_module_args(default_args) + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['failed'] is True + assert all(txt in results['msg'] for txt in ( + 'missing', + 'required', + 'access_token', + )) + + +def test_mandatory_access_token_passed_in_env(default_args, + access_token): + set_module_args(default_args) + + try: + module = linode_v4.initialise_module() + except SystemExit: + pytest.fail("'access_token' is passed in environment") + + now_set_token = module.params['access_token'] + assert now_set_token == os.environ['LINODE_ACCESS_TOKEN'] + + +def test_mandatory_access_token_passed_in_as_parameter(default_args, + no_access_token_in_env): + default_args.update({'access_token': 'foo'}) + set_module_args(default_args) + + try: + module = linode_v4.initialise_module() + except SystemExit: + pytest.fail("'access_token' is passed in as parameter") + + assert module.params['access_token'] == 'foo' + + +def test_instance_by_label_cannot_authenticate(capfd, access_token, + default_args): + set_module_args(default_args) + module = linode_v4.initialise_module() + client = LinodeClient(module.params['access_token']) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, side_effect=LinodeApiError('foo')): + with pytest.raises(SystemExit): + linode_v4.maybe_instance_from_label(module, client) + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['failed'] is True + assert 'Unable to query the Linode API' in results['msg'] + + +def test_no_instances_found_with_label_gives_none(default_args, + access_token): + set_module_args(default_args) + module = linode_v4.initialise_module() + client = LinodeClient(module.params['access_token']) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[]): + result = linode_v4.maybe_instance_from_label(module, client) + + assert result is None + + +def test_optional_region_is_validated(default_args, capfd, access_token): + default_args.update({'type': 'foo', 'image': 'bar'}) + set_module_args(default_args) + + with pytest.raises(SystemExit): + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['failed'] is True + assert all(txt in results['msg'] for txt in ( + 'required', + 'together', + 'region' + )) + + +def test_optional_type_is_validated(default_args, capfd, access_token): + default_args.update({'region': 'foo', 'image': 'bar'}) + set_module_args(default_args) + + with pytest.raises(SystemExit): + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['failed'] is True + assert all(txt in results['msg'] for txt in ( + 'required', + 'together', + 'type' + )) + + +def test_optional_image_is_validated(default_args, capfd, access_token): + default_args.update({'type': 'foo', 'region': 'bar'}) + set_module_args(default_args) + + with pytest.raises(SystemExit): + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['failed'] is True + assert all(txt in results['msg'] for txt in ( + 'required', + 'together', + 'image' + )) + + +@pytest.mark.parametrize('value', [True, False]) +def test_private_ip_valid_values(default_args, access_token, value): + default_args.update({'private_ip': value}) + set_module_args(default_args) + + module = linode_v4.initialise_module() + + assert module.params['private_ip'] is value + + +@pytest.mark.parametrize('value', ['not-a-bool', 42]) +def test_private_ip_invalid_values(default_args, capfd, access_token, value): + default_args.update({'private_ip': value}) + set_module_args(default_args) + + with pytest.raises(SystemExit): + linode_v4.initialise_module() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['failed'] is True + assert 'not a valid boolean' in results['msg'] + + +def test_private_ip_default_value(default_args, access_token): + default_args.pop('private_ip', None) + set_module_args(default_args) + + module = linode_v4.initialise_module() + + assert module.params['private_ip'] is False + + +def test_private_ip_is_forwarded_to_linode(default_args, mock_linode, access_token): + default_args.update({'private_ip': True}) + set_module_args(default_args) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[]): + with pytest.raises(SystemExit): + target = 'linode_api4.linode_client.LinodeGroup.instance_create' + with mock.patch(target, return_value=(mock_linode, 'passw0rd')) as instance_create_mock: + linode_v4.main() + + args, kwargs = instance_create_mock.call_args + assert kwargs['private_ip'] is True + + +def test_instance_already_created(default_args, + mock_linode, + capfd, + access_token): + default_args.update({ + 'type': 'foo', + 'region': 'bar', + 'image': 'baz' + }) + set_module_args(default_args) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[mock_linode]): + with pytest.raises(SystemExit) as sys_exit_exc: + linode_v4.main() + + assert sys_exit_exc.value.code == 0 + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['changed'] is False + assert 'root_password' not in results['instance'] + assert ( + results['instance']['label'] == + mock_linode._raw_json['label'] + ) + + +def test_instance_to_be_created_without_root_pass(default_args, + mock_linode, + capfd, + access_token): + default_args.update({ + 'type': 'foo', + 'region': 'bar', + 'image': 'baz' + }) + set_module_args(default_args) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[]): + with pytest.raises(SystemExit) as sys_exit_exc: + target = 'linode_api4.linode_client.LinodeGroup.instance_create' + with mock.patch(target, return_value=(mock_linode, 'passw0rd')): + linode_v4.main() + + assert sys_exit_exc.value.code == 0 + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['changed'] is True + assert ( + results['instance']['label'] == + mock_linode._raw_json['label'] + ) + assert results['instance']['root_pass'] == 'passw0rd' + + +def test_instance_to_be_created_with_root_pass(default_args, + mock_linode, + capfd, + access_token): + default_args.update({ + 'type': 'foo', + 'region': 'bar', + 'image': 'baz', + 'root_pass': 'passw0rd', + }) + set_module_args(default_args) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[]): + with pytest.raises(SystemExit) as sys_exit_exc: + target = 'linode_api4.linode_client.LinodeGroup.instance_create' + with mock.patch(target, return_value=mock_linode): + linode_v4.main() + + assert sys_exit_exc.value.code == 0 + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['changed'] is True + assert ( + results['instance']['label'] == + mock_linode._raw_json['label'] + ) + assert 'root_pass' not in results['instance'] + + +def test_instance_to_be_deleted(default_args, + mock_linode, + capfd, + access_token): + default_args.update({'state': 'absent'}) + set_module_args(default_args) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[mock_linode]): + with pytest.raises(SystemExit) as sys_exit_exc: + linode_v4.main() + + assert sys_exit_exc.value.code == 0 + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['changed'] is True + assert ( + results['instance']['label'] == + mock_linode._raw_json['label'] + ) + + +def test_instance_already_deleted_no_change(default_args, + mock_linode, + capfd, + access_token): + default_args.update({'state': 'absent'}) + set_module_args(default_args) + + target = 'linode_api4.linode_client.LinodeGroup.instances' + with mock.patch(target, return_value=[]): + with pytest.raises(SystemExit) as sys_exit_exc: + linode_v4.main() + + assert sys_exit_exc.value.code == 0 + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results['changed'] is False + assert results['instance'] == {} + + +def test_user_agent_created_properly(): + try: + from ansible.module_utils.ansible_release import ( + __version__ as ansible_version + ) + except ImportError: + ansible_version = 'unknown' + + expected_user_agent = 'Ansible-linode_v4_module/%s' % ansible_version + assert expected_user_agent == get_user_agent('linode_v4_module') diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_cmms.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_cmms.py new file mode 100644 index 000000000..efbdad062 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_cmms.py @@ -0,0 +1,101 @@ +# 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 + +import pytest +from ansible_collections.community.general.tests.unit.compat import mock +from ansible_collections.community.general.plugins.modules import lxca_cmms + + +@pytest.fixture(scope='module') +@mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.close_conn', autospec=True) +def setup_module(close_conn): + close_conn.return_value = True + + +class TestMyModule(): + @pytest.mark.parametrize('patch_ansible_module', + [ + {}, + { + "auth_url": "https://10.240.14.195", + "login_user": "USERID", + }, + { + "auth_url": "https://10.240.14.195", + "login_password": "Password", + }, + { + "login_user": "USERID", + "login_password": "Password", + }, + ], + indirect=['patch_ansible_module']) + @pytest.mark.usefixtures('patch_ansible_module') + @mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.setup_conn', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_cmms.execute_module', autospec=True) + def test_without_required_parameters(self, _setup_conn, _execute_module, + mocker, capfd, setup_module): + """Failure must occurs when all parameters are missing""" + with pytest.raises(SystemExit): + _setup_conn.return_value = "Fake connection" + _execute_module.return_value = "Fake execution" + lxca_cmms.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'missing required arguments' in results['msg'] + + @mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.setup_conn', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_cmms.execute_module', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_cmms.AnsibleModule', autospec=True) + def test__argument_spec(self, ansible_mod_cls, _execute_module, _setup_conn, setup_module): + expected_arguments_spec = dict( + login_user=dict(required=True), + login_password=dict(required=True, no_log=True), + command_options=dict(default='cmms', choices=['cmms', 'cmms_by_uuid', + 'cmms_by_chassis_uuid']), + auth_url=dict(required=True), + uuid=dict(default=None), + chassis=dict(default=None), + ) + _setup_conn.return_value = "Fake connection" + _execute_module.return_value = [] + mod_obj = ansible_mod_cls.return_value + args = { + "auth_url": "https://10.243.30.195", + "login_user": "USERID", + "login_password": "password", + "command_options": "cmms", + } + mod_obj.params = args + lxca_cmms.main() + assert mock.call(argument_spec=expected_arguments_spec, + supports_check_mode=False) == ansible_mod_cls.call_args + + @mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.setup_conn', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_cmms._cmms_by_uuid', + autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_cmms.AnsibleModule', + autospec=True) + def test__cmms_empty_list(self, ansible_mod_cls, _get_cmms, _setup_conn, setup_module): + mod_obj = ansible_mod_cls.return_value + args = { + "auth_url": "https://10.243.30.195", + "login_user": "USERID", + "login_password": "password", + "uuid": "3C737AA5E31640CE949B10C129A8B01F", + "command_options": "cmms_by_uuid", + } + mod_obj.params = args + _setup_conn.return_value = "Fake connection" + empty_nodes_list = [] + _get_cmms.return_value = empty_nodes_list + ret_cmms = _get_cmms(mod_obj, args) + assert mock.call(mod_obj, mod_obj.params) == _get_cmms.call_args + assert _get_cmms.return_value == ret_cmms diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_nodes.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_nodes.py new file mode 100644 index 000000000..87effa0c0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_lxca_nodes.py @@ -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 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +import pytest +from ansible_collections.community.general.tests.unit.compat import mock +from ansible_collections.community.general.plugins.modules import lxca_nodes + + +@pytest.fixture(scope='module') +@mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.close_conn', autospec=True) +def setup_module(close_conn): + close_conn.return_value = True + + +class TestMyModule(): + @pytest.mark.parametrize('patch_ansible_module', + [ + {}, + { + "auth_url": "https://10.240.14.195", + "login_user": "USERID", + }, + { + "auth_url": "https://10.240.14.195", + "login_password": "Password", + }, + { + "login_user": "USERID", + "login_password": "Password", + }, + ], + indirect=['patch_ansible_module']) + @pytest.mark.usefixtures('patch_ansible_module') + @mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.setup_conn', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_nodes.execute_module', autospec=True) + def test_without_required_parameters(self, _setup_conn, _execute_module, + mocker, capfd, setup_module): + """Failure must occurs when all parameters are missing""" + with pytest.raises(SystemExit): + _setup_conn.return_value = "Fake connection" + _execute_module.return_value = "Fake execution" + lxca_nodes.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'missing required arguments' in results['msg'] + + @mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.setup_conn', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_nodes.execute_module', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_nodes.AnsibleModule', autospec=True) + def test__argument_spec(self, ansible_mod_cls, _execute_module, _setup_conn, setup_module): + expected_arguments_spec = dict( + login_user=dict(required=True), + login_password=dict(required=True, no_log=True), + command_options=dict(default='nodes', choices=['nodes', 'nodes_by_uuid', + 'nodes_by_chassis_uuid', + 'nodes_status_managed', + 'nodes_status_unmanaged']), + auth_url=dict(required=True), + uuid=dict(default=None), + chassis=dict(default=None), + ) + _setup_conn.return_value = "Fake connection" + _execute_module.return_value = [] + mod_obj = ansible_mod_cls.return_value + args = { + "auth_url": "https://10.243.30.195", + "login_user": "USERID", + "login_password": "password", + "command_options": "nodes", + } + mod_obj.params = args + lxca_nodes.main() + assert mock.call(argument_spec=expected_arguments_spec, + supports_check_mode=False) == ansible_mod_cls.call_args + + @mock.patch('ansible_collections.community.general.plugins.module_utils.remote_management.lxca.common.setup_conn', autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_nodes._nodes_by_uuid', + autospec=True) + @mock.patch('ansible_collections.community.general.plugins.modules.lxca_nodes.AnsibleModule', + autospec=True) + def test__nodes_empty_list(self, ansible_mod_cls, _get_nodes, _setup_conn, setup_module): + mod_obj = ansible_mod_cls.return_value + args = { + "auth_url": "https://10.243.30.195", + "login_user": "USERID", + "login_password": "password", + "uuid": "3C737AA5E31640CE949B10C129A8B01F", + "command_options": "nodes_by_uuid", + } + mod_obj.params = args + _setup_conn.return_value = "Fake connection" + empty_nodes_list = [] + _get_nodes.return_value = empty_nodes_list + ret_nodes = _get_nodes(mod_obj, args) + assert mock.call(mod_obj, mod_obj.params) == _get_nodes.call_args + assert _get_nodes.return_value == ret_nodes diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_macports.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_macports.py new file mode 100644 index 000000000..61de27654 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_macports.py @@ -0,0 +1,35 @@ +# 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.plugins.modules import macports + +import pytest + +TESTED_MODULE = macports.__name__ + +QUERY_PORT_TEST_CASES = [ + pytest.param('', False, False, id='Not installed'), + pytest.param(' git @2.29.2_0+credential_osxkeychain+diff_highlight+doc+pcre+perl5_28', True, False, id='Installed but not active'), + pytest.param(' git @2.29.2_0+credential_osxkeychain+diff_highlight+doc+pcre+perl5_28 (active)', True, True, id='Installed and active'), + pytest.param(''' git @2.29.2_0+credential_osxkeychain+diff_highlight+doc+pcre+perl5_28 + git @2.28.1_0+credential_osxkeychain+diff_highlight+doc+pcre+perl5_28 +''', True, False, id='2 versions installed, neither active'), + pytest.param(''' git @2.29.2_0+credential_osxkeychain+diff_highlight+doc+pcre+perl5_28 (active) + git @2.28.1_0+credential_osxkeychain+diff_highlight+doc+pcre+perl5_28 +''', True, True, id='2 versions installed, one active'), +] + + +@pytest.mark.parametrize("run_cmd_return_val, present_expected, active_expected", QUERY_PORT_TEST_CASES) +def test_macports_query_port(mocker, run_cmd_return_val, present_expected, active_expected): + module = mocker.Mock() + run_command = mocker.Mock() + run_command.return_value = (0, run_cmd_return_val, '') + module.run_command = run_command + + assert macports.query_port(module, 'port', 'git', state="present") == present_expected + assert macports.query_port(module, 'port', 'git', state="active") == active_expected diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_maven_artifact.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_maven_artifact.py new file mode 100644 index 000000000..7e2557449 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_maven_artifact.py @@ -0,0 +1,71 @@ +# Copyright (c) 2017 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 pytest + +from ansible_collections.community.general.plugins.modules import maven_artifact +from ansible.module_utils import basic + + +pytestmark = pytest.mark.usefixtures('patch_ansible_module') + +maven_metadata_example = b""" + + junit + junit + + 4.13-beta-2 + 4.13-beta-2 + + 3.7 + 3.8 + 3.8.1 + 3.8.2 + 4.0 + 4.1 + 4.2 + 4.3 + 4.3.1 + 4.4 + 4.5 + 4.6 + 4.7 + 4.8 + 4.8.1 + 4.8.2 + 4.9 + 4.10 + 4.11-beta-1 + 4.11 + 4.12-beta-1 + 4.12-beta-2 + 4.12-beta-3 + 4.12 + 4.13-beta-1 + 4.13-beta-2 + + 20190202141051 + + +""" + + +@pytest.mark.parametrize('patch_ansible_module, version_by_spec, version_choosed', [ + (None, "(,3.9]", "3.8.2"), + (None, "3.0", "3.8.2"), + (None, "[3.7]", "3.7"), + (None, "[4.10, 4.12]", "4.12"), + (None, "[4.10, 4.12)", "4.11"), + (None, "[2.0,)", "4.13-beta-2"), +]) +def test_find_version_by_spec(mocker, version_by_spec, version_choosed): + _getContent = mocker.patch('ansible_collections.community.general.plugins.modules.maven_artifact.MavenDownloader._getContent') + _getContent.return_value = maven_metadata_example + + artifact = maven_artifact.Artifact("junit", "junit", None, version_by_spec, "jar") + mvn_downloader = maven_artifact.MavenDownloader(basic.AnsibleModule, "https://repo1.maven.org/maven2") + + assert mvn_downloader.find_version_by_spec(artifact) == version_choosed 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 new file mode 100644 index 000000000..18695695a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py @@ -0,0 +1,485 @@ +# -*- 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 sys +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ModuleTestCase, set_module_args +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.compat.mock import Mock +from ansible_collections.community.general.tests.unit.compat.mock import mock_open +from ansible_collections.community.general.plugins.modules.modprobe import Modprobe, build_module + + +class TestLoadModule(ModuleTestCase): + def setUp(self): + super(TestLoadModule, self).setUp() + + self.mock_module_loaded = patch( + 'ansible_collections.community.general.plugins.modules.modprobe.Modprobe.module_loaded' + ) + self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command') + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.module_loaded = self.mock_module_loaded.start() + self.run_command = self.mock_run_command.start() + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestLoadModule, self).tearDown() + self.mock_module_loaded.stop() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + + def test_load_module_success(self): + set_module_args(dict( + name='test', + state='present', + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + self.module_loaded.side_effect = [True] + self.run_command.side_effect = [(0, '', '')] + + modprobe = Modprobe(module) + modprobe.load_module() + + assert modprobe.result == { + 'changed': True, + 'name': 'test', + 'params': '', + 'state': 'present', + } + + def test_load_module_unchanged(self): + set_module_args(dict( + name='test', + state='present', + )) + + module = build_module() + + module.warn = Mock() + + self.get_bin_path.side_effect = ['modprobe'] + self.module_loaded.side_effect = [False] + self.run_command.side_effect = [(0, '', ''), (1, '', '')] + + modprobe = Modprobe(module) + modprobe.load_module() + + module.warn.assert_called_once_with('') + + +class TestUnloadModule(ModuleTestCase): + def setUp(self): + super(TestUnloadModule, self).setUp() + + self.mock_module_loaded = patch( + 'ansible_collections.community.general.plugins.modules.modprobe.Modprobe.module_loaded' + ) + self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command') + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.module_loaded = self.mock_module_loaded.start() + self.run_command = self.mock_run_command.start() + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestUnloadModule, self).tearDown() + self.mock_module_loaded.stop() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + + def test_unload_module_success(self): + set_module_args(dict( + name='test', + state='absent', + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + self.module_loaded.side_effect = [False] + self.run_command.side_effect = [(0, '', '')] + + modprobe = Modprobe(module) + modprobe.unload_module() + + assert modprobe.result == { + 'changed': True, + 'name': 'test', + 'params': '', + 'state': 'absent', + } + + def test_unload_module_failure(self): + set_module_args(dict( + name='test', + state='absent', + )) + + module = build_module() + + module.fail_json = Mock() + + self.get_bin_path.side_effect = ['modprobe'] + self.module_loaded.side_effect = [True] + self.run_command.side_effect = [(1, '', '')] + + modprobe = Modprobe(module) + modprobe.unload_module() + + dummy_result = { + 'changed': False, + 'name': 'test', + 'state': 'absent', + 'params': '', + } + + module.fail_json.assert_called_once_with( + msg='', rc=1, stdout='', stderr='', **dummy_result + ) + + +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') + + super(TestModuleIsLoadedPersistently, self).setUp() + + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestModuleIsLoadedPersistently, self).tearDown() + + self.mock_get_bin_path.stop() + + def test_module_is_loaded(self): + + set_module_args(dict( + name='dummy', + state='present', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data='dummy')) as mocked_file: + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modules_files'): + modprobe.modules_files = ['/etc/modules-load.d/dummy.conf'] + + assert modprobe.module_is_loaded_persistently + + mocked_file.assert_called_once_with('/etc/modules-load.d/dummy.conf') + + def test_module_is_not_loaded_empty_file(self): + + set_module_args(dict( + name='dummy', + state='present', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data='')) as mocked_file: + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modules_files'): + modprobe.modules_files = ['/etc/modules-load.d/dummy.conf'] + + assert not modprobe.module_is_loaded_persistently + + mocked_file.assert_called_once_with('/etc/modules-load.d/dummy.conf') + + def test_module_is_not_loaded_no_files(self): + + set_module_args(dict( + name='dummy', + state='present', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modules_files'): + modprobe.modules_files = [] + + assert not modprobe.module_is_loaded_persistently + + +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') + super(TestPermanentParams, self).setUp() + + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestPermanentParams, self).tearDown() + + self.mock_get_bin_path.stop() + + def test_module_permanent_params_exist(self): + + files_content = [ + 'options dummy numdummies=4\noptions dummy dummy_parameter1=6', + 'options dummy dummy_parameter2=5 #Comment\noptions notdummy notdummy_param=5' + ] + mock_files_content = [mock_open(read_data=content).return_value for content in files_content] + + set_module_args(dict( + name='dummy', + state='present', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open()) as mocked_file: + mocked_file.side_effect = mock_files_content + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modprobe_files'): + modprobe.modprobe_files = ['/etc/modprobe.d/dummy1.conf', '/etc/modprobe.d/dummy2.conf'] + + assert modprobe.permanent_params == set(['numdummies=4', 'dummy_parameter1=6', 'dummy_parameter2=5']) + + def test_module_permanent_params_empty(self): + + files_content = [ + '', + '' + ] + mock_files_content = [mock_open(read_data=content).return_value for content in files_content] + + set_module_args(dict( + name='dummy', + state='present', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data='')) as mocked_file: + mocked_file.side_effect = mock_files_content + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modprobe_files'): + modprobe.modprobe_files = ['/etc/modprobe.d/dummy1.conf', '/etc/modprobe.d/dummy2.conf'] + + assert modprobe.permanent_params == set() + + +class TestCreateModuleFIle(ModuleTestCase): + def setUp(self): + super(TestCreateModuleFIle, self).setUp() + + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestCreateModuleFIle, self).tearDown() + + self.mock_get_bin_path.stop() + + def test_create_file(self): + + set_module_args(dict( + name='dummy', + state='present', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open()) as mocked_file: + modprobe.create_module_file() + mocked_file.assert_called_once_with('/etc/modules-load.d/dummy.conf', 'w') + mocked_file().write.assert_called_once_with('dummy\n') + + +class TestCreateModuleOptionsFIle(ModuleTestCase): + def setUp(self): + super(TestCreateModuleOptionsFIle, self).setUp() + + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestCreateModuleOptionsFIle, self).tearDown() + + self.mock_get_bin_path.stop() + + def test_create_file(self): + + set_module_args(dict( + name='dummy', + state='present', + params='numdummies=4', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open()) as mocked_file: + modprobe.create_module_options_file() + mocked_file.assert_called_once_with('/etc/modprobe.d/dummy.conf', 'w') + mocked_file().write.assert_called_once_with('options dummy numdummies=4\n') + + +class TestDisableOldParams(ModuleTestCase): + def setUp(self): + super(TestDisableOldParams, self).setUp() + + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestDisableOldParams, self).tearDown() + + self.mock_get_bin_path.stop() + + def test_disable_old_params_file_changed(self): + mock_data = 'options dummy numdummies=4' + + set_module_args(dict( + name='dummy', + state='present', + params='numdummies=4', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data=mock_data)) as mocked_file: + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modprobe_files'): + modprobe.modprobe_files = ['/etc/modprobe.d/dummy1.conf'] + modprobe.disable_old_params() + mocked_file.assert_called_with('/etc/modprobe.d/dummy1.conf', 'w') + mocked_file().write.assert_called_once_with('#options dummy numdummies=4') + + def test_disable_old_params_file_unchanged(self): + mock_data = 'options notdummy numdummies=4' + + set_module_args(dict( + name='dummy', + state='present', + params='numdummies=4', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data=mock_data)) as mocked_file: + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modprobe_files'): + modprobe.modprobe_files = ['/etc/modprobe.d/dummy1.conf'] + modprobe.disable_old_params() + mocked_file.assert_called_once_with('/etc/modprobe.d/dummy1.conf') + + +class TestDisableModulePermanent(ModuleTestCase): + def setUp(self): + super(TestDisableModulePermanent, self).setUp() + + self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + """Teardown.""" + super(TestDisableModulePermanent, self).tearDown() + + self.mock_get_bin_path.stop() + + def test_disable_module_permanent_file_changed(self): + + set_module_args(dict( + name='dummy', + state='present', + params='numdummies=4', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data='dummy')) as mocked_file: + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modules_files'): + modprobe.modules_files = ['/etc/modules-load.d/dummy.conf'] + modprobe.disable_module_permanent() + mocked_file.assert_called_with('/etc/modules-load.d/dummy.conf', 'w') + mocked_file().write.assert_called_once_with('#dummy') + + def test_disable_module_permanent_file_unchanged(self): + + set_module_args(dict( + name='dummy', + state='present', + params='numdummies=4', + persistent='present' + )) + + module = build_module() + + self.get_bin_path.side_effect = ['modprobe'] + + modprobe = Modprobe(module) + + with patch('ansible_collections.community.general.plugins.modules.modprobe.open', mock_open(read_data='notdummy')) as mocked_file: + with patch('ansible_collections.community.general.plugins.modules.modprobe.Modprobe.modules_files'): + modprobe.modules_files = ['/etc/modules-load.d/dummy.conf'] + modprobe.disable_module_permanent() + mocked_file.assert_called_once_with('/etc/modules-load.d/dummy.conf') diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_monit.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_monit.py new file mode 100644 index 000000000..7f8f15dd9 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_monit.py @@ -0,0 +1,159 @@ +# 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 mock +import pytest + +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.plugins.modules import monit +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson + + +TEST_OUTPUT = """ +%s '%s' + status %s + monitoring status Not monitored + monitoring mode active +""" + + +class MonitTest(unittest.TestCase): + def setUp(self): + self.module = mock.MagicMock() + self.module.exit_json.side_effect = AnsibleExitJson + self.module.fail_json.side_effect = AnsibleFailJson + self.monit = monit.Monit(self.module, 'monit', 'processX', 1) + self.monit._status_change_retry_count = 1 + mock_sleep = mock.patch('time.sleep') + mock_sleep.start() + self.addCleanup(mock_sleep.stop) + + def patch_status(self, side_effect): + if not isinstance(side_effect, list): + side_effect = [side_effect] + return mock.patch.object(self.monit, 'get_status', side_effect=side_effect) + + def test_change_state_success(self): + with self.patch_status([monit.Status.OK, monit.Status.NOT_MONITORED]): + with self.assertRaises(AnsibleExitJson): + self.monit.stop() + self.module.fail_json.assert_not_called() + self.module.run_command.assert_called_with(['monit', 'stop', 'processX'], check_rc=True) + + def test_change_state_fail(self): + with self.patch_status([monit.Status.OK] * 3): + with self.assertRaises(AnsibleFailJson): + self.monit.stop() + + def test_reload_fail(self): + self.module.run_command.return_value = (1, 'stdout', 'stderr') + with self.assertRaises(AnsibleFailJson): + self.monit.reload() + + def test_reload(self): + self.module.run_command.return_value = (0, '', '') + with self.patch_status(monit.Status.OK): + with self.assertRaises(AnsibleExitJson): + self.monit.reload() + + def test_wait_for_status_to_stop_pending(self): + status = [ + monit.Status.MISSING, + monit.Status.DOES_NOT_EXIST, + monit.Status.INITIALIZING, + monit.Status.OK.pending(), + monit.Status.OK + ] + with self.patch_status(status) as get_status: + self.monit.wait_for_monit_to_stop_pending() + self.assertEqual(get_status.call_count, len(status)) + + def test_wait_for_status_change(self): + with self.patch_status([monit.Status.NOT_MONITORED, monit.Status.OK]) as get_status: + self.monit.wait_for_status_change(monit.Status.NOT_MONITORED) + self.assertEqual(get_status.call_count, 2) + + def test_wait_for_status_change_fail(self): + with self.patch_status([monit.Status.OK] * 3): + with self.assertRaises(AnsibleFailJson): + self.monit.wait_for_status_change(monit.Status.OK) + + def test_monitor(self): + with self.patch_status([monit.Status.NOT_MONITORED, monit.Status.OK.pending(), monit.Status.OK]): + with self.assertRaises(AnsibleExitJson): + self.monit.monitor() + + def test_monitor_fail(self): + with self.patch_status([monit.Status.NOT_MONITORED] * 3): + with self.assertRaises(AnsibleFailJson): + self.monit.monitor() + + def test_timeout(self): + self.monit.timeout = 0 + with self.patch_status(monit.Status.NOT_MONITORED.pending()): + with self.assertRaises(AnsibleFailJson): + self.monit.wait_for_monit_to_stop_pending() + + +@pytest.mark.parametrize('status_name', monit.StatusValue.ALL_STATUS) +def test_status_value(status_name): + value = getattr(monit.StatusValue, status_name.upper()) + status = monit.StatusValue(value) + assert getattr(status, 'is_%s' % status_name) + assert not all(getattr(status, 'is_%s' % name) for name in monit.StatusValue.ALL_STATUS if name != status_name) + + +BASIC_OUTPUT_CASES = [ + (TEST_OUTPUT % ('Process', 'processX', name), getattr(monit.Status, name.upper())) + for name in monit.StatusValue.ALL_STATUS +] + + +@pytest.mark.parametrize('output, expected', BASIC_OUTPUT_CASES + [ + ('', monit.Status.MISSING), + (TEST_OUTPUT % ('Process', 'processY', 'OK'), monit.Status.MISSING), + (TEST_OUTPUT % ('Process', 'processX', 'Not Monitored - start pending'), monit.Status.OK), + (TEST_OUTPUT % ('Process', 'processX', 'Monitored - stop pending'), monit.Status.NOT_MONITORED), + (TEST_OUTPUT % ('Process', 'processX', 'Monitored - restart pending'), monit.Status.OK), + (TEST_OUTPUT % ('Process', 'processX', 'Not Monitored - monitor pending'), monit.Status.OK), + (TEST_OUTPUT % ('Process', 'processX', 'Does not exist'), monit.Status.DOES_NOT_EXIST), + (TEST_OUTPUT % ('Process', 'processX', 'Not monitored'), monit.Status.NOT_MONITORED), + (TEST_OUTPUT % ('Process', 'processX', 'Running'), monit.Status.OK), + (TEST_OUTPUT % ('Process', 'processX', 'Execution failed | Does not exist'), monit.Status.EXECUTION_FAILED), +]) +def test_parse_status(output, expected): + status = monit.Monit(None, '', 'processX', 0)._parse_status(output, '') + assert status == expected + + +@pytest.mark.parametrize('output, expected', BASIC_OUTPUT_CASES + [ + (TEST_OUTPUT % ('Process', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('File', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Fifo', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Filesystem', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Directory', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Remote host', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('System', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Program', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Network', 'processX', 'OK'), monit.Status.OK), + (TEST_OUTPUT % ('Unsupported', 'processX', 'OK'), monit.Status.MISSING), +]) +def test_parse_status_supports_all_services(output, expected): + status = monit.Monit(None, '', 'processX', 0)._parse_status(output, '') + assert status == expected + + +@pytest.mark.parametrize('output, expected', [ + ('This is monit version 5.18.1', '5.18.1'), + ('This is monit version 12.18', '12.18'), + ('This is monit version 5.1.12', '5.1.12'), +]) +def test_parse_version(output, expected): + module = mock.MagicMock() + module.run_command.return_value = (0, output, '') + raw_version, version_tuple = monit.Monit(module, '', 'processX', 0)._get_monit_version() + assert raw_version == expected 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 new file mode 100644 index 000000000..efd8284a3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py @@ -0,0 +1,4261 @@ +# Copyright (c) 2017 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 + +import pytest + +from ansible.module_utils.common.text.converters import to_text +from ansible_collections.community.general.plugins.modules import nmcli +from ansible.module_utils.basic import AnsibleModule + +pytestmark = pytest.mark.usefixtures('patch_ansible_module') + +TESTCASE_CONNECTION = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'generic', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'bond', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'bond-slave', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'bridge', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'vlan', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'vxlan', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'gre', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'ipip', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'sit', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'dummy', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'gsm', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'wireguard', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'vpn', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'infiniband', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, + { + 'type': 'macvlan', + 'conn_name': 'non_existent_nw_device', + 'state': 'absent', + '_ansible_check_mode': True, + }, +] + +TESTCASE_GENERIC = [ + { + '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', + '_ansible_check_mode': False, + }, +] + +TESTCASE_GENERIC_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.route-metric: -1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_GENERIC_DIFF_CHECK = [ + { + 'type': 'generic', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'generic_non_existant', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.2', + 'route_metric4': -1, + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_GENERIC_MODIFY_ROUTING_RULES = [ + { + 'type': 'generic', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'generic_non_existant', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'routing_rules4': ['priority 5 from 10.0.0.0/24 table 5000', 'priority 10 from 10.0.1.0/24 table 5001'], + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_GENERIC_MODIFY_ROUTING_RULES_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.routing-rules: priority 5 from 10.0.0.0/24 table 5000, priority 10 from 10.0.1.0/24 table 5001 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6': ['fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2'], + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', + 'next_hop': '2001:beef:cafe:10::2'}], + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +ipv4.method: auto +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: manual +ipv6.addresses: 2001:beef:cafe:10::1/64 +ipv6.routes: { ip = fd2e:446f:d85d:5::/64, nh = 2001:beef:cafe:10::2 } +ipv6.route-metric: -1 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'routes4': ['192.168.200.0/24 192.168.1.1'], + 'route_metric4': 10, + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'routes4_extended': [{'ip': '192.168.200.0/24', 'next_hop': '192.168.1.1'}], + 'route_metric4': 10, + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +ipv4.method: manual +ipv4.addresses: 192.168.1.10 +ipv4.routes: { ip = 192.168.200.0/24, nh = 192.168.1.1 } +ipv4.route-metric: 10 +""" + +TESTCASE_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'routes6': ['fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2'], + 'route_metric6': 10, + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'}], + 'route_metric6': 10, + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +ipv6.method: manual +ipv6.addresses: 2001:beef:cafe:10::1/64 +ipv6.routes: { ip = fd2e:446f:d85d:5::/64, nh = 2001:beef:cafe:10::2 } +ipv6.route-metric 10 +""" + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6': ['fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2', 'fd2e:8890:abcd:25::/64 2001:beef:cafe:10::5'], + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'}, + {'ip': 'fd2e:8890:abcd:25::/64', 'next_hop': '2001:beef:cafe:10::5'}], + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +ipv4.method: auto +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: manual +ipv6.addresses: 2001:beef:cafe:10::1/64 +ipv6.routes: { ip = fd2e:446f:d85d:5::/64, nh = 2001:beef:cafe:10::2 }; { ip = fd2e:8890:abcd:25::/64, nh = 2001:beef:cafe:10::5 } +ipv6.route-metric: -1 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'method4': 'disabled', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6': ['fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2'], + 'route_metric6': 5, + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'method4': 'disabled', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'}], + 'route_metric6': 5, + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +ipv4.method: auto +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: manual +ipv6.addresses: 2001:beef:cafe:10::1/64 +ipv6.routes: { ip = fd2e:446f:d85d:5::/64, nh = 2001:beef:cafe:10::2 } +ipv6.route-metric: 5 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_AND_METRIC = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'method4': 'disabled', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6': ['fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2', 'fd2e:8890:abcd:25::/64 2001:beef:cafe:10::5'], + 'route_metric6': 5, + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'method4': 'disabled', + 'ip6': '2001:beef:cafe:10::1/64', + 'routes6_extended': [{'ip': 'fd2e:446f:d85d:5::/64', 'next_hop': '2001:beef:cafe:10::2'}, + {'ip': 'fd2e:8890:abcd:25::/64', 'next_hop': '2001:beef:cafe:10::5'}], + 'route_metric6': 5, + 'method6': 'manual', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_AND_METRIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +ipv4.method: auto +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: manual +ipv6.addresses: 2001:beef:cafe:10::1/64 +ipv6.routes: { ip = fd2e:446f:d85d:5::/64, nh = 2001:beef:cafe:10::2 }; { ip = fd2e:8890:abcd:25::/64, nh = 2001:beef:cafe:10::5 } +ipv6.route-metric: 5 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_GENERIC_DNS4_SEARCH = [ + { + '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_search': 'search.redhat.com', + 'dns6_search': 'search6.redhat.com', + '_ansible_check_mode': False, + } +] + +TESTCASE_GENERIC_DNS4_SEARCH_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-search: search.redhat.com +ipv4.may-fail: yes +ipv6.dns-search: search6.redhat.com +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_GENERIC_ZONE = [ + { + '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', + 'zone': 'external', + '_ansible_check_mode': False, + } +] + +TESTCASE_GENERIC_ZONE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: generic_non_existant +connection.autoconnect: yes +connection.zone: external +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.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_GENERIC_ZONE_ONLY = [ + { + 'type': 'generic', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'generic_non_existant', + 'state': 'present', + 'zone': 'public', + '_ansible_check_mode': False, + } +] + +TESTCASE_BOND = [ + { + 'type': 'bond', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'bond_non_existant', + 'mode': 'active-backup', + 'xmit_hash_policy': 'layer3+4', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'state': 'present', + 'primary': 'non_existent_primary', + '_ansible_check_mode': False, + } +] + +TESTCASE_BOND_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: bond_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.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +bond.options: mode=active-backup,primary=non_existent_primary,xmit_hash_policy=layer3+4 +""" + +TESTCASE_BRIDGE = [ + { + 'type': 'bridge', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'br0_non_existant', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'mac': '52:54:00:ab:cd:ef', + 'maxage': 100, + 'stp': True, + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_BRIDGE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: br0_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.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +bridge.mac-address: 52:54:00:AB:CD:EF +bridge.stp: yes +bridge.max-age: 100 +bridge.ageing-time: 300 +bridge.hello-time: 2 +bridge.priority: 128 +bridge.forward-delay: 15 +""" + +TESTCASE_BRIDGE_SLAVE = [ + { + 'type': 'bridge-slave', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'br0_non_existant', + 'path_cost': 100, + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_BRIDGE_SLAVE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: br0_non_existant +connection.autoconnect: yes +connection.slave-type: bridge +ipv4.never-default: no +bridge-port.path-cost: 100 +bridge-port.hairpin-mode: yes +bridge-port.priority: 32 +""" + +TESTCASE_TEAM = [ + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_TEAM_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: team0_non_existant +connection.autoconnect: yes +connection.type: team +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +team.runner: roundrobin +team.runner-fast-rate: no +""" + +TESTCASE_TEAM_HWADDR_POLICY_FAILS = [ + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'runner_hwaddr_policy': 'by_active', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_TEAM_RUNNER_FAST_RATE = [ + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'runner': 'lacp', + 'runner_fast_rate': True, + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_TEAM_RUNNER_FAST_RATE_FAILS = [ + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'runner_fast_rate': True, + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'state': 'present', + 'runner_fast_rate': False, + '_ansible_check_mode': False, + }, + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'state': 'present', + 'runner': 'activebackup', + 'runner_fast_rate': False, + '_ansible_check_mode': False, + }, + { + 'type': 'team', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'team0_non_existant', + 'state': 'present', + 'runner': 'activebackup', + 'runner_fast_rate': True, + '_ansible_check_mode': False, + } +] + +TESTCASE_TEAM_RUNNER_FAST_RATE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: team0_non_existant +connection.autoconnect: yes +connection.type: team +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +team.runner: lacp +team.runner-fast-rate: yes +""" + +TESTCASE_TEAM_SLAVE = [ + { + 'type': 'team-slave', + 'conn_name': 'non_existent_nw_slaved_device', + 'ifname': 'generic_slaved_non_existant', + 'master': 'team0_non_existant', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_TEAM_SLAVE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_slaved_device +connection.interface-name: generic_slaved_non_existant +connection.autoconnect: yes +connection.master: team0_non_existant +connection.slave-type: team +802-3-ethernet.mtu: auto +""" + +TESTCASE_VLAN = [ + { + 'type': 'vlan', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'vlan_not_exists', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'vlanid': 10, + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_VLAN_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: vlan_not_exists +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.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +vlan.id: 10 +802-3-ethernet.mtu: auto +""" + +TESTCASE_VXLAN = [ + { + 'type': 'vxlan', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'vxlan-existent_nw_device', + 'vxlan_id': 11, + 'vxlan_local': '192.168.225.5', + 'vxlan_remote': '192.168.225.6', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_VXLAN_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: vxlan-existent_nw_device +connection.autoconnect: yes +vxlan.id: 11 +vxlan.local: 192.168.225.5 +vxlan.remote: 192.168.225.6 +""" + +TESTCASE_GRE = [ + { + 'type': 'gre', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'gre-existent_nw_device', + 'ip_tunnel_dev': 'non_existent_gre_device', + 'ip_tunnel_local': '192.168.225.5', + 'ip_tunnel_remote': '192.168.225.6', + 'ip_tunnel_input_key': '1', + 'ip_tunnel_output_key': '2', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_GRE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: gre-existent_nw_device +connection.autoconnect: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ip-tunnel.mode: gre +ip-tunnel.parent: non_existent_gre_device +ip-tunnel.local: 192.168.225.5 +ip-tunnel.remote: 192.168.225.6 +ip-tunnel.input-key: 1 +ip-tunnel.output-key: 2 +""" + +TESTCASE_IPIP = [ + { + 'type': 'ipip', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ipip-existent_nw_device', + 'ip_tunnel_dev': 'non_existent_ipip_device', + 'ip_tunnel_local': '192.168.225.5', + 'ip_tunnel_remote': '192.168.225.6', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_IPIP_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ipip-existent_nw_device +connection.autoconnect: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ip-tunnel.mode: ipip +ip-tunnel.parent: non_existent_ipip_device +ip-tunnel.local: 192.168.225.5 +ip-tunnel.remote: 192.168.225.6 +""" + +TESTCASE_SIT = [ + { + 'type': 'sit', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'sit-existent_nw_device', + 'ip_tunnel_dev': 'non_existent_sit_device', + 'ip_tunnel_local': '192.168.225.5', + 'ip_tunnel_remote': '192.168.225.6', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_SIT_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: sit-existent_nw_device +connection.autoconnect: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ip-tunnel.mode: sit +ip-tunnel.parent: non_existent_sit_device +ip-tunnel.local: 192.168.225.5 +ip-tunnel.remote: 192.168.225.6 +""" + +TESTCASE_ETHERNET_DHCP = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'dhcp_client_id': '00:11:22:AA:BB:CC:DD', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_ETHERNET_DHCP_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv4.method: auto +ipv4.dhcp-client-id: 00:11:22:AA:BB:CC:DD +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_STATIC = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'dns4': ['1.1.1.1', '8.8.8.8'], + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +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.may-fail: yes +ipv4.dns: 1.1.1.1,8.8.8.8 +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip4': ['10.10.10.10/32', '10.10.20.10/32'], + 'gw4': '10.10.10.1', + 'dns4': ['1.1.1.1', '8.8.8.8'], + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip4': ['10.10.10.10', '10.10.20.10'], + 'gw4': '10.10.10.1', + 'dns4': ['1.1.1.1', '8.8.8.8'], + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_ETHERNET_STATIC_IP6_PRIVACY_AND_ADDR_GEN_MODE = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': '2001:db8::cafe/128', + 'gw6': '2001:db8::cafa', + 'dns6': ['2001:4860:4860::8888'], + 'state': 'present', + 'ip_privacy6': 'prefer-public-addr', + 'addr_gen_mode6': 'eui64', + '_ansible_check_mode': False, + } +] + +TESTCASE_ETHERNET_STATIC_MULTIPLE_IP6_ADDRESSES = [ + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': ['2001:db8::cafe/128', '2002:db8::cafe/128'], + 'gw6': '2001:db8::cafa', + 'dns6': ['2001:4860:4860::8888', '2001:4860:4860::8844'], + 'state': 'present', + '_ansible_check_mode': False, + }, + { + 'type': 'ethernet', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'ethernet_non_existant', + 'ip6': ['2001:db8::cafe', '2002:db8::cafe'], + 'gw6': '2001:db8::cafa', + 'dns6': ['2001:4860:4860::8888', '2001:4860:4860::8844'], + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv4.method: manual +ipv4.addresses: 10.10.10.10/32, 10.10.20.10/32 +ipv4.gateway: 10.10.10.1 +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv4.dns: 1.1.1.1,8.8.8.8 +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +""" + +TESTCASE_ETHERNET_STATIC_IP6_ADDRESS_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv6.method: manual +ipv6.addresses: 2001:db8::cafe/128 +ipv6.gateway: 2001:db8::cafa +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.never-default: no +ipv6.may-fail: yes +ipv6.dns: 2001:4860:4860::8888,2001:4860:4860::8844 +ipv4.method: disabled +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +""" + + +TESTCASE_ETHERNET_STATIC_MULTIPLE_IP6_ADDRESSES_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv6.method: manual +ipv6.addresses: 2001:db8::cafe/128, 2002:db8::cafe/128 +ipv6.gateway: 2001:db8::cafa +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.never-default: no +ipv6.may-fail: yes +ipv6.dns: 2001:4860:4860::8888,2001:4860:4860::8844 +ipv4.method: disabled +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +""" + +TESTCASE_WIRELESS = [ + { + 'type': 'wifi', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'wireless_non_existant', + 'ip4': '10.10.10.10/24', + 'ssid': 'Brittany', + 'wifi': { + 'hidden': True, + 'mode': 'ap', + }, + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_SECURE_WIRELESS = [ + { + 'type': 'wifi', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'wireless_non_existant', + 'ip4': '10.10.10.10/24', + 'ssid': 'Brittany', + 'wifi_sec': { + 'key-mgmt': 'wpa-psk', + 'psk': 'VERY_SECURE_PASSWORD', + }, + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_DEFAULT_WIRELESS_SHOW_OUTPUT = """\ +802-11-wireless.ssid: -- +802-11-wireless.mode: infrastructure +802-11-wireless.band: -- +802-11-wireless.channel: 0 +802-11-wireless.bssid: -- +802-11-wireless.rate: 0 +802-11-wireless.tx-power: 0 +802-11-wireless.mac-address: -- +802-11-wireless.cloned-mac-address: -- +802-11-wireless.generate-mac-address-mask:-- +802-11-wireless.mac-address-blacklist: -- +802-11-wireless.mac-address-randomization:default +802-11-wireless.mtu: auto +802-11-wireless.seen-bssids: -- +802-11-wireless.hidden: no +802-11-wireless.powersave: 0 (default) +802-11-wireless.wake-on-wlan: 0x1 (default) +802-11-wireless.ap-isolation: -1 (default) +""" + +TESTCASE_DEFAULT_SECURE_WIRELESS_SHOW_OUTPUT = \ + TESTCASE_DEFAULT_WIRELESS_SHOW_OUTPUT + """\ +802-11-wireless-security.key-mgmt: -- +802-11-wireless-security.wep-tx-keyidx: 0 +802-11-wireless-security.auth-alg: -- +802-11-wireless-security.proto: -- +802-11-wireless-security.pairwise: -- +802-11-wireless-security.group: -- +802-11-wireless-security.pmf: 0 (default) +802-11-wireless-security.leap-username: -- +802-11-wireless-security.wep-key0: -- +802-11-wireless-security.wep-key1: -- +802-11-wireless-security.wep-key2: -- +802-11-wireless-security.wep-key3: -- +802-11-wireless-security.wep-key-flags: 0 (none) +802-11-wireless-security.wep-key-type: unknown +802-11-wireless-security.psk: testingtestingtesting +802-11-wireless-security.psk-flags: 0 (none) +802-11-wireless-security.leap-password: -- +802-11-wireless-security.leap-password-flags:0 (none) +802-11-wireless-security.wps-method: 0x0 (default) +802-11-wireless-security.fils: 0 (default) +""" + + +TESTCASE_DUMMY_STATIC = [ + { + 'type': 'dummy', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'dummy_non_existant', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'dns4': ['1.1.1.1', '8.8.8.8'], + 'ip6': '2001:db8::1/128', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_DUMMY_STATIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: dummy_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +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.may-fail: yes +ipv4.dns: 1.1.1.1,8.8.8.8 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.method: manual +ipv6.addresses: 2001:db8::1/128 +""" + +TESTCASE_DUMMY_STATIC_WITHOUT_MTU_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: dummy_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.may-fail: yes +ipv4.dns: 1.1.1.1,8.8.8.8 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.method: manual +ipv6.addresses: 2001:db8::1/128 +""" + +TESTCASE_DUMMY_STATIC_WITH_CUSTOM_MTU_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: dummy_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: 1500 +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.may-fail: yes +ipv4.dns: 1.1.1.1,8.8.8.8 +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.method: manual +ipv6.addresses: 2001:db8::1/128 +""" + +TESTCASE_ETHERNET_STATIC_IP6_PRIVACY_AND_ADDR_GEN_MODE_UNCHANGED_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: ethernet_non_existant +connection.autoconnect: yes +802-3-ethernet.mtu: auto +ipv6.method: manual +ipv6.addresses: 2001:db8::cafe/128 +ipv6.gateway: 2001:db8::cafa +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +ipv6.never-default: no +ipv6.may-fail: yes +ipv6.ip6-privacy: 1 (enabled, prefer public IP) +ipv6.addr-gen-mode: eui64 +ipv6.dns: 2001:4860:4860::8888 +ipv4.method: disabled +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +""" + +TESTCASE_GSM = [ + { + 'type': 'gsm', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'gsm_non_existant', + 'gsm': { + 'apn': 'internet.telekom', + 'username': 't-mobile', + 'password': 'tm', + 'pin': '1234', + }, + 'method4': 'auto', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_GSM_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.type: gsm +connection.interface-name: gsm_non_existant +connection.autoconnect: yes +ipv4.method: auto +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv4.never-default: no +ipv4.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +gsm.auto-config: no +gsm.number: -- +gsm.username: t-mobile +gsm.password: tm +gsm.password-flags: 0 (none) +gsm.apn: "internet.telekom" +gsm.network-id: -- +gsm.pin: 1234 +gsm.pin-flags: 0 (none) +gsm.home-only: no +gsm.device-id: -- +gsm.sim-id: -- +gsm.sim-operator-id: -- +gsm.mtu: auto +""" + +TESTCASE_WIREGUARD = [ + { + 'type': 'wireguard', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'wg_non_existant', + 'wireguard': { + 'listen-port': '51820', + 'private-key': '', + }, + 'method4': 'manual', + 'ip4': '10.10.10.10/24', + 'method6': 'manual', + 'ip6': '2001:db8::1/128', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_WIREGUARD_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.type: wireguard +connection.interface-name: wg_non_existant +connection.autoconnect: yes +ipv4.method: manual +ipv4.addresses: 10.10.10.10/24 +ipv4.never-default: no +ipv4.may-fail: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv6.method: manual +ipv6.addresses: 2001:db8::1/128 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +wireguard.private-key: +wireguard.private-key-flags: 0 (none) +wireguard.listen-port: 51820 +wireguard.fwmark: 0x0 +wireguard.peer-routes: yes +wireguard.mtu: 0 +wireguard.ip4-auto-default-route: -1 (default) +wireguard.ip6-auto-default-route: -1 (default) +""" + +TESTCASE_VPN_L2TP = [ + { + 'type': 'vpn', + 'conn_name': 'vpn_l2tp', + 'vpn': { + 'permissions': 'brittany', + 'service-type': 'org.freedesktop.NetworkManager.l2tp', + 'gateway': 'vpn.example.com', + 'password-flags': '2', + 'user': 'brittany', + 'ipsec-enabled': 'true', + 'ipsec-psk': 'QnJpdHRhbnkxMjM=', + }, + 'gw4_ignore_auto': True, + 'routes4': ['192.168.200.0/24'], + 'autoconnect': 'false', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_VPN_L2TP_SHOW_OUTPUT = """\ +connection.id: vpn_l2tp +connection.type: vpn +connection.autoconnect: no +connection.permissions: brittany +ipv4.method: auto +ipv4.routes: { ip = 192.168.200.0/24 } +ipv4.never-default: no +ipv4.may-fail: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +vpn.service-type: org.freedesktop.NetworkManager.l2tp +vpn.data: gateway = vpn.example.com, ipsec-enabled = true, ipsec-psk = QnJpdHRhbnkxMjM=, password-flags = 2, user = brittany +vpn.secrets: ipsec-psk = QnJpdHRhbnkxMjM= +vpn.persistent: no +vpn.timeout: 0 +""" + +TESTCASE_VPN_PPTP = [ + { + 'type': 'vpn', + 'conn_name': 'vpn_pptp', + 'vpn': { + 'permissions': 'brittany', + 'service-type': 'org.freedesktop.NetworkManager.pptp', + 'gateway': 'vpn.example.com', + 'password-flags': '2', + 'user': 'brittany', + }, + 'autoconnect': 'false', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_VPN_PPTP_SHOW_OUTPUT = """\ +connection.id: vpn_pptp +connection.type: vpn +connection.autoconnect: no +connection.permissions: brittany +ipv4.method: auto +ipv4.never-default: no +ipv4.may-fail: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +vpn.service-type: org.freedesktop.NetworkManager.pptp +vpn.data: gateway=vpn.example.com, password-flags=2, user=brittany +""" + +TESTCASE_INFINIBAND_STATIC = [ + { + 'type': 'infiniband', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'infiniband_non_existant', + 'ip4': '10.10.10.10/24', + 'gw4': '10.10.10.1', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_INFINIBAND_STATIC_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.type: infiniband +connection.interface-name: infiniband_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.may-fail: yes +ipv6.method: auto +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +infiniband.transport-mode datagram +""" + +TESTCASE_INFINIBAND_STATIC_MODIFY_TRANSPORT_MODE = [ + { + + 'type': 'infiniband', + 'conn_name': 'non_existent_nw_device', + 'transport_mode': 'connected', + 'state': 'present', + '_ansible_check_mode': False, + }, +] + +TESTCASE_INFINIBAND_STATIC_MODIFY_TRANSPORT_MODE_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.interface-name: infiniband_non_existant +infiniband.transport_mode: connected +""" + +TESTCASE_MACVLAN = [ + { + 'type': 'macvlan', + 'conn_name': 'non_existent_nw_device', + 'ifname': 'macvlan_non_existant', + 'macvlan': { + 'mode': '2', + 'parent': 'non_existent_parent', + }, + 'method4': 'manual', + 'ip4': '10.10.10.10/24', + 'method6': 'manual', + 'ip6': '2001:db8::1/128', + 'state': 'present', + '_ansible_check_mode': False, + } +] + +TESTCASE_MACVLAN_SHOW_OUTPUT = """\ +connection.id: non_existent_nw_device +connection.type: macvlan +connection.interface-name: macvlan_non_existant +connection.autoconnect: yes +ipv4.method: manual +ipv4.addresses: 10.10.10.10/24 +ipv4.never-default: no +ipv4.may-fail: yes +ipv4.ignore-auto-dns: no +ipv4.ignore-auto-routes: no +ipv6.method: manual +ipv6.addresses: 2001:db8::1/128 +ipv6.ignore-auto-dns: no +ipv6.ignore-auto-routes: no +macvlan.parent: non_existent_parent +macvlan.mode: 2 (bridge) +macvlan.promiscuous: yes +macvlan.tap: no +""" + + +def mocker_set(mocker, + connection_exists=False, + execute_return=(0, "", ""), + execute_side_effect=None, + changed_return=None): + """ + Common mocker object + """ + get_bin_path = mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path') + get_bin_path.return_value = '/usr/bin/nmcli' + connection = mocker.patch.object(nmcli.Nmcli, 'connection_exists') + connection.return_value = connection_exists + execute_command = mocker.patch.object(nmcli.Nmcli, 'execute_command') + if execute_return: + execute_command.return_value = execute_return + if execute_side_effect: + execute_command.side_effect = execute_side_effect + if changed_return: + is_connection_changed = mocker.patch.object(nmcli.Nmcli, 'is_connection_changed') + is_connection_changed.return_value = changed_return + + +@pytest.fixture +def mocked_generic_connection_create(mocker): + mocker_set(mocker) + + +@pytest.fixture +def mocked_connection_exists(mocker): + mocker_set(mocker, connection_exists=True) + + +@pytest.fixture +def mocked_generic_connection_modify(mocker): + mocker_set(mocker, + connection_exists=True, + changed_return=(True, dict())) + + +@pytest.fixture +def mocked_generic_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GENERIC_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_generic_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GENERIC_MODIFY_ROUTING_RULES_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_generic_connection_dns_search_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GENERIC_DNS4_SEARCH_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_generic_connection_zone_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GENERIC_ZONE_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_bond_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_BOND_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_bridge_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_BRIDGE_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_bridge_slave_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_BRIDGE_SLAVE_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_team_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_TEAM_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_team_runner_fast_rate_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_TEAM_RUNNER_FAST_RATE_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_team_slave_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_TEAM_SLAVE_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_vlan_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_VLAN_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_vxlan_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_VXLAN_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_gre_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GRE_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ipip_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_IPIP_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_sit_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_SIT_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_DHCP, "")) + + +@pytest.fixture +def mocked_ethernet_connection_dhcp_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_DHCP_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_static_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_static_multiple_ip4_addresses_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_static_ip6_privacy_and_addr_gen_mode_unchange(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_STATIC_IP6_PRIVACY_AND_ADDR_GEN_MODE_UNCHANGED_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_static_multiple_ip6_addresses_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_ETHERNET_STATIC_MULTIPLE_IP6_ADDRESSES_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_ethernet_connection_static_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_with_ipv6_static_address_static_route_create(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_with_ipv4_static_address_static_route_metric_modify(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, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_with_ipv6_static_address_multiple_static_routes_create(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_with_ipv6_static_address_static_route_with_metric_create(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_with_ipv6_static_address_multiple_static_routes_with_metric_create(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_AND_METRIC_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_with_ipv6_address_static_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_STATIC_IP6_ADDRESS_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_ethernet_connection_dhcp_to_static(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_ETHERNET_DHCP_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_wireless_create(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_DEFAULT_WIRELESS_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_secure_wireless_create(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_DEFAULT_SECURE_WIRELESS_SHOW_OUTPUT, ""), + (0, "", ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_secure_wireless_create_failure(mocker): + mocker_set(mocker, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_DEFAULT_SECURE_WIRELESS_SHOW_OUTPUT, ""), + (1, "", ""), + )) + + +@pytest.fixture +def mocked_secure_wireless_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_DEFAULT_SECURE_WIRELESS_SHOW_OUTPUT, ""), + (0, "", ""), + (0, "", ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_secure_wireless_modify_failure(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_DEFAULT_SECURE_WIRELESS_SHOW_OUTPUT, ""), + (0, "", ""), + (1, "", ""), + )) + + +@pytest.fixture +def mocked_dummy_connection_static_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_DUMMY_STATIC_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_dummy_connection_static_without_mtu_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_DUMMY_STATIC_WITHOUT_MTU_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_dummy_connection_static_with_custom_mtu_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_DUMMY_STATIC_WITH_CUSTOM_MTU_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_gsm_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GSM_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_wireguard_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_WIREGUARD_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_vpn_l2tp_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_VPN_L2TP_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_vpn_pptp_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_VPN_PPTP_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_infiniband_connection_static_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_INFINIBAND_STATIC_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_infiniband_connection_static_transport_mode_connected_modify(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=None, + execute_side_effect=( + (0, TESTCASE_INFINIBAND_STATIC_MODIFY_TRANSPORT_MODE_SHOW_OUTPUT, ""), + (0, "", ""), + )) + + +@pytest.fixture +def mocked_macvlan_connection_unchanged(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_MACVLAN_SHOW_OUTPUT, "")) + + +@pytest.fixture +def mocked_generic_connection_diff_check(mocker): + mocker_set(mocker, + connection_exists=True, + execute_return=(0, TESTCASE_GENERIC_SHOW_OUTPUT, "")) + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BOND, indirect=['patch_ansible_module']) +def test_bond_connection_create(mocked_generic_connection_create, capfd): + """ + Test : Bond connection 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] == 'bond' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + for param in ['ipv4.gateway', 'primary', 'connection.autoconnect', + 'connection.interface-name', 'bond_non_existant', + 'mode', 'active-backup', 'ipv4.addresses', + '+bond.options', 'xmit_hash_policy=layer3+4']: + assert param 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_BOND, indirect=['patch_ansible_module']) +def test_bond_connection_unchanged(mocked_bond_connection_unchanged, capfd): + """ + Test : Bond 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_GENERIC, indirect=['patch_ansible_module']) +def test_generic_connection_create(mocked_generic_connection_create, capfd): + """ + Test : Generic connection 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] == 'generic' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + for param in ['connection.autoconnect', 'ipv4.gateway', 'ipv4.addresses']: + assert param 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, indirect=['patch_ansible_module']) +def test_generic_connection_modify(mocked_generic_connection_modify, capfd): + """ + Test : Generic connection modify + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + for param in ['ipv4.gateway', 'ipv4.addresses']: + assert param 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, indirect=['patch_ansible_module']) +def test_generic_connection_unchanged(mocked_generic_connection_unchanged, capfd): + """ + Test : Generic 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_GENERIC_MODIFY_ROUTING_RULES, indirect=['patch_ansible_module']) +def test_generic_connection_modify_routing_rules4(mocked_generic_connection_create, capfd): + """ + Test : Generic connection modified with routing-rules4 + """ + 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.routing-rules' 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_SEARCH, indirect=['patch_ansible_module']) +def test_generic_connection_create_dns_search(mocked_generic_connection_create, capfd): + """ + Test : Generic connection created with dns search + """ + 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-search' in args[0] + assert 'ipv6.dns-search' 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_SEARCH, indirect=['patch_ansible_module']) +def test_generic_connection_modify_dns_search(mocked_generic_connection_create, capfd): + """ + Test : Generic connection modified with dns search + """ + 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-search' in args[0] + assert 'ipv6.dns-search' 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_SEARCH, indirect=['patch_ansible_module']) +def test_generic_connection_dns_search_unchanged(mocked_generic_connection_dns_search_unchanged, capfd): + """ + Test : Generic connection with dns search 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): + """ + Test if DNS4 param is None + """ + with pytest.raises(SystemExit): + nmcli.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_ZONE, indirect=['patch_ansible_module']) +def test_generic_connection_create_zone(mocked_generic_connection_create, capfd): + """ + Test : Generic connection created with zone + """ + 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 'connection.zone' 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_ZONE, indirect=['patch_ansible_module']) +def test_generic_connection_modify_zone(mocked_generic_connection_create, capfd): + """ + Test : Generic connection modified with zone + """ + 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 'connection.zone' 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_ZONE, indirect=['patch_ansible_module']) +def test_generic_connection_zone_unchanged(mocked_generic_connection_zone_unchanged, capfd): + """ + Test : Generic connection with zone 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_GENERIC_ZONE_ONLY, indirect=['patch_ansible_module']) +def test_generic_connection_modify_zone_only(mocked_generic_connection_modify, capfd): + """ + Test : Generic connection modified with zone only + """ + 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 'connection.zone' in args[0] + assert 'ipv4.addresses' not in args[0] + assert 'ipv4.gateway' not in args[0] + assert 'ipv6.addresses' not in args[0] + assert 'ipv6.gateway' not 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_CONNECTION, indirect=['patch_ansible_module']) +def test_zone_none(mocked_connection_exists, capfd): + """ + Test if zone param is None + """ + with pytest.raises(SystemExit): + nmcli.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BRIDGE, indirect=['patch_ansible_module']) +def test_create_bridge(mocked_generic_connection_create, capfd): + """ + Test if 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] == 'bridge' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ipv4.addresses', '10.10.10.10/24', 'ipv4.gateway', '10.10.10.1', 'bridge.max-age', '100', 'bridge.stp', 'yes']: + assert param in 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_BRIDGE, indirect=['patch_ansible_module']) +def test_mod_bridge(mocked_generic_connection_modify, capfd): + """ + Test if Bridge modified + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ipv4.addresses', '10.10.10.10/24', 'ipv4.gateway', '10.10.10.1', 'bridge.max-age', '100', 'bridge.stp', 'yes']: + assert param in 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_BRIDGE, indirect=['patch_ansible_module']) +def test_bridge_connection_unchanged(mocked_bridge_connection_unchanged, capfd): + """ + Test : Bridge 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_BRIDGE_SLAVE, indirect=['patch_ansible_module']) +def test_create_bridge_slave(mocked_generic_connection_create, capfd): + """ + Test if Bridge_slave 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] == 'bridge-slave' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['bridge-port.path-cost', '100']: + assert param in 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_BRIDGE_SLAVE, indirect=['patch_ansible_module']) +def test_mod_bridge_slave(mocked_generic_connection_modify, capfd): + """ + Test if Bridge_slave modified + """ + + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['bridge-port.path-cost', '100']: + assert param in 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_BRIDGE_SLAVE, indirect=['patch_ansible_module']) +def test_bridge_slave_unchanged(mocked_bridge_slave_unchanged, capfd): + """ + Test : Bridge-slave 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_TEAM, indirect=['patch_ansible_module']) +def test_team_connection_create(mocked_generic_connection_create, capfd): + """ + Test : Team connection 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] == 'team' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + for param in ['connection.autoconnect', 'connection.interface-name', 'team0_non_existant']: + assert param 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_TEAM, indirect=['patch_ansible_module']) +def test_team_connection_unchanged(mocked_team_connection_unchanged, capfd): + """ + Test : Team 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_TEAM_HWADDR_POLICY_FAILS, indirect=['patch_ansible_module']) +def test_team_connection_create_hwaddr_policy_fails(mocked_generic_connection_create, capfd): + """ + Test : Team connection created + """ + with pytest.raises(SystemExit): + nmcli.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert results.get('failed') + assert results['msg'] == "Runner-hwaddr-policy is only allowed for runner activebackup" + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_TEAM_RUNNER_FAST_RATE, indirect=['patch_ansible_module']) +def test_team_runner_fast_rate_connection_create(mocked_generic_connection_create, capfd): + """ + Test : Team connection created with runner_fast_rate parameter + """ + 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] == 'team' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + for param in ['connection.autoconnect', 'connection.interface-name', 'team0_non_existant', 'team.runner', 'lacp', 'team.runner-fast-rate', 'yes']: + assert param 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_TEAM_RUNNER_FAST_RATE, indirect=['patch_ansible_module']) +def test_team_runner_fast_rate_connection_unchanged(mocked_team_runner_fast_rate_connection_unchanged, capfd): + """ + Test : Team connection unchanged with runner_fast_rate parameter + """ + 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_TEAM_RUNNER_FAST_RATE_FAILS, indirect=['patch_ansible_module']) +def test_team_connection_create_runner_fast_rate_fails(mocked_generic_connection_create, capfd): + """ + Test : Team connection with runner_fast_rate enabled + """ + with pytest.raises(SystemExit): + nmcli.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert results.get('failed') + assert results['msg'] == "runner-fast-rate is only allowed for runner lacp" + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_TEAM_SLAVE, indirect=['patch_ansible_module']) +def test_create_team_slave(mocked_generic_connection_create, capfd): + """ + Test if Team_slave 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] == 'team-slave' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_slaved_device' + + for param in ['connection.autoconnect', 'connection.interface-name', 'connection.master', 'team0_non_existant', 'connection.slave-type']: + assert param 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_TEAM_SLAVE, indirect=['patch_ansible_module']) +def test_team_slave_connection_unchanged(mocked_team_slave_connection_unchanged, capfd): + """ + Test : Team slave 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_VLAN, indirect=['patch_ansible_module']) +def test_create_vlan_con(mocked_generic_connection_create, capfd): + """ + Test if VLAN 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] == 'vlan' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ipv4.addresses', '10.10.10.10/24', 'ipv4.gateway', '10.10.10.1', 'vlan.id', '10']: + assert param in 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_VLAN, indirect=['patch_ansible_module']) +def test_mod_vlan_conn(mocked_generic_connection_modify, capfd): + """ + Test if VLAN modified + """ + + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ipv4.addresses', '10.10.10.10/24', 'ipv4.gateway', '10.10.10.1', 'vlan.id', '10']: + assert param in 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_VLAN, indirect=['patch_ansible_module']) +def test_vlan_connection_unchanged(mocked_vlan_connection_unchanged, capfd): + """ + Test : VLAN 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_VXLAN, indirect=['patch_ansible_module']) +def test_create_vxlan(mocked_generic_connection_create, capfd): + """ + Test if vxlan 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] == 'vxlan' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['connection.interface-name', 'vxlan-existent_nw_device', + 'vxlan.local', '192.168.225.5', 'vxlan.remote', '192.168.225.6', 'vxlan.id', '11']: + assert param in 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_VXLAN, indirect=['patch_ansible_module']) +def test_vxlan_mod(mocked_generic_connection_modify, capfd): + """ + Test if vxlan modified + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['vxlan.local', '192.168.225.5', 'vxlan.remote', '192.168.225.6', 'vxlan.id', '11']: + assert param in 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_VXLAN, indirect=['patch_ansible_module']) +def test_vxlan_connection_unchanged(mocked_vxlan_connection_unchanged, capfd): + """ + Test : VxLAN 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_IPIP, indirect=['patch_ansible_module']) +def test_create_ipip(mocked_generic_connection_create, capfd): + """ + Test if ipip 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] == 'ip-tunnel' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['connection.interface-name', 'ipip-existent_nw_device', + 'ip-tunnel.local', '192.168.225.5', + 'ip-tunnel.mode', 'ipip', + 'ip-tunnel.parent', 'non_existent_ipip_device', + 'ip-tunnel.remote', '192.168.225.6']: + assert param in 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_IPIP, indirect=['patch_ansible_module']) +def test_ipip_mod(mocked_generic_connection_modify, capfd): + """ + Test if ipip modified + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']: + assert param in 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_IPIP, indirect=['patch_ansible_module']) +def test_ipip_connection_unchanged(mocked_ipip_connection_unchanged, capfd): + """ + Test : IPIP 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_SIT, indirect=['patch_ansible_module']) +def test_create_sit(mocked_generic_connection_create, capfd): + """ + Test if sit 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] == 'ip-tunnel' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['connection.interface-name', 'sit-existent_nw_device', + 'ip-tunnel.local', '192.168.225.5', + 'ip-tunnel.mode', 'sit', + 'ip-tunnel.parent', 'non_existent_sit_device', + 'ip-tunnel.remote', '192.168.225.6']: + assert param in 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_SIT, indirect=['patch_ansible_module']) +def test_sit_mod(mocked_generic_connection_modify, capfd): + """ + Test if sit modified + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']: + assert param in 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_SIT, indirect=['patch_ansible_module']) +def test_sit_connection_unchanged(mocked_sit_connection_unchanged, capfd): + """ + Test : SIT 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_ETHERNET_DHCP, indirect=['patch_ansible_module']) +def test_eth_dhcp_client_id_con_create(mocked_generic_connection_create, capfd): + """ + Test : Ethernet connection created with DHCP_CLIENT_ID + """ + 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.dhcp-client-id' 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_GRE, indirect=['patch_ansible_module']) +def test_create_gre(mocked_generic_connection_create, capfd): + """ + Test if gre 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] == 'ip-tunnel' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['connection.interface-name', 'gre-existent_nw_device', + 'ip-tunnel.local', '192.168.225.5', + 'ip-tunnel.mode', 'gre', + 'ip-tunnel.parent', 'non_existent_gre_device', + 'ip-tunnel.remote', '192.168.225.6', + 'ip-tunnel.input-key', '1', + 'ip-tunnel.output-key', '2']: + assert param in 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_GRE, indirect=['patch_ansible_module']) +def test_gre_mod(mocked_generic_connection_modify, capfd): + """ + Test if gre modified + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['ip-tunnel.local', '192.168.225.5', 'ip-tunnel.remote', '192.168.225.6']: + assert param in 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_GRE, indirect=['patch_ansible_module']) +def test_gre_connection_unchanged(mocked_gre_connection_unchanged, capfd): + """ + Test : GRE 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_ETHERNET_DHCP, indirect=['patch_ansible_module']) +def test_ethernet_connection_dhcp_unchanged(mocked_ethernet_connection_dhcp_unchanged, capfd): + """ + Test : Ethernet connection with DHCP_CLIENT_ID 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_ETHERNET_STATIC, indirect=['patch_ansible_module']) +def test_modify_ethernet_dhcp_to_static(mocked_ethernet_connection_dhcp_to_static, capfd): + """ + Test : Modify ethernet connection from DHCP to static + """ + 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] == 'non_existent_nw_device' + + for param in ['ipv4.method', 'ipv4.gateway', 'ipv4.addresses']: + assert param 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_ETHERNET_STATIC, indirect=['patch_ansible_module']) +def test_create_ethernet_static(mocked_generic_connection_create, capfd): + """ + Test : Create ethernet connection with static IP configuration + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'ethernet_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + 'ipv4.gateway', '10.10.10.1', + 'ipv4.dns', '1.1.1.1,8.8.8.8']: + assert param in add_args_text + + up_args, up_kw = arg_list[1] + assert up_args[0][0] == '/usr/bin/nmcli' + assert up_args[0][1] == 'con' + assert up_args[0][2] == 'up' + assert up_args[0][3] == 'non_existent_nw_device' + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_unchanged(mocked_ethernet_connection_static_unchanged, capfd): + """ + Test : Ethernet connection with static IP configuration 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_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_ipv4_address_static_route_with_metric_modify( + mocked_ethernet_connection_with_ipv4_static_address_static_route_metric_modify, 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', '192.168.200.0/24 192.168.1.1', + 'ipv4.route-metric', '10']: + assert param in add_args_text + + out, err = capfd.readouterr() + results = json.loads(out) + + 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): + """ + Test : Create ethernet connection with static IPv6 address and static route + """ + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + + for param in ['connection.interface-name', 'ethernet_non_existant', + 'con-name', 'non_existent_nw_device', + 'ipv6.addresses', '2001:beef:cafe:10::1/64', + 'ipv6.method', 'manual', + 'ipv6.routes', 'fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2']: + 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_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_ipv6_address_static_route_metric_modify( + mocked_ethernet_connection_with_ipv6_static_address_static_route_metric_modify, capfd): + """ + Test : Modify ethernet connection with static IPv6 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 ['ipv6.routes', 'fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2', + 'ipv6.route-metric', '10']: + assert param in add_args_text + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results.get('changed') is True + assert not results.get('failed') + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_ipv6_address_multiple_static_routes_with_metric_create( + mocked_ethernet_connection_with_ipv6_static_address_multiple_static_routes_with_metric_create, capfd): + """ + Test : Create ethernet connection with static IPv6 address and multiple static routes + """ + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + + for param in ['connection.interface-name', 'ethernet_non_existant', + 'con-name', 'non_existent_nw_device', + 'ipv6.addresses', '2001:beef:cafe:10::1/64', + 'ipv6.method', 'manual', + 'ipv6.routes', 'fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2,fd2e:8890:abcd:25::/64 2001:beef:cafe:10::5']: + 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_ETHERNET_ADD_IPV6_INT_WITH_ROUTE_AND_METRIC, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_ipv6_address_static_route_with_metric_create( + mocked_ethernet_connection_with_ipv6_static_address_static_route_with_metric_create, capfd): + """ + Test : Create ethernet connection with static IPv6 address and static route with metric + """ + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + + for param in ['connection.interface-name', 'ethernet_non_existant', + 'con-name', 'non_existent_nw_device', + 'ipv6.addresses', '2001:beef:cafe:10::1/64', + 'ipv6.method', 'manual', + 'ipv6.routes', 'fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2', + 'ipv6.route-metric', '5']: + 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_ETHERNET_ADD_IPV6_INT_WITH_MULTIPLE_ROUTES_AND_METRIC, 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): + """ + Test : Create ethernet connection with static IPv6 address and multiple static routes with metric + """ + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + + for param in ['connection.interface-name', 'ethernet_non_existant', + 'con-name', 'non_existent_nw_device', + 'ipv6.addresses', '2001:beef:cafe:10::1/64', + 'ipv6.method', 'manual', + 'ipv6.routes', 'fd2e:446f:d85d:5::/64 2001:beef:cafe:10::2,fd2e:8890:abcd:25::/64 2001:beef:cafe:10::5', + 'ipv6.route-metric', '5']: + 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_WIRELESS, indirect=['patch_ansible_module']) +def test_create_wireless(mocked_wireless_create, capfd): + """ + Test : Create wireless connection + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + arg_list = nmcli.Nmcli.execute_command.call_args_list + + get_available_options_args, get_available_options_kw = arg_list[0] + assert get_available_options_args[0][0] == '/usr/bin/nmcli' + assert get_available_options_args[0][1] == 'con' + assert get_available_options_args[0][2] == 'edit' + assert get_available_options_args[0][3] == 'type' + assert get_available_options_args[0][4] == 'wifi' + + get_available_options_data = get_available_options_kw['data'].split() + for param in ['print', '802-11-wireless', + 'quit', 'yes']: + assert param in get_available_options_data + + 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] == 'add' + assert add_args[0][3] == 'type' + assert add_args[0][4] == 'wifi' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'wireless_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + '802-11-wireless.ssid', 'Brittany', + '802-11-wireless.mode', 'ap', + '802-11-wireless.hidden', 'yes']: + 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_SECURE_WIRELESS, indirect=['patch_ansible_module']) +def test_create_secure_wireless(mocked_secure_wireless_create, capfd): + """ + Test : Create secure wireless connection + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 3 + arg_list = nmcli.Nmcli.execute_command.call_args_list + + get_available_options_args, get_available_options_kw = arg_list[0] + assert get_available_options_args[0][0] == '/usr/bin/nmcli' + assert get_available_options_args[0][1] == 'con' + assert get_available_options_args[0][2] == 'edit' + assert get_available_options_args[0][3] == 'type' + assert get_available_options_args[0][4] == 'wifi' + + get_available_options_data = get_available_options_kw['data'].split() + for param in ['print', '802-11-wireless-security', + 'quit', 'yes']: + assert param in get_available_options_data + + 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] == 'add' + assert add_args[0][3] == 'type' + assert add_args[0][4] == 'wifi' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'wireless_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + '802-11-wireless.ssid', 'Brittany', + '802-11-wireless-security.key-mgmt', 'wpa-psk']: + assert param in add_args_text + + edit_args, edit_kw = arg_list[2] + assert edit_args[0][0] == '/usr/bin/nmcli' + assert edit_args[0][1] == 'con' + assert edit_args[0][2] == 'edit' + assert edit_args[0][3] == 'non_existent_nw_device' + + edit_kw_data = edit_kw['data'].split() + for param in ['802-11-wireless-security.psk', 'VERY_SECURE_PASSWORD', + 'save', + 'quit']: + assert param in edit_kw_data + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SECURE_WIRELESS, indirect=['patch_ansible_module']) +def test_create_secure_wireless_failure(mocked_secure_wireless_create_failure, capfd): + """ + Test : Create secure wireless connection w/failure + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + arg_list = nmcli.Nmcli.execute_command.call_args_list + + get_available_options_args, get_available_options_kw = arg_list[0] + assert get_available_options_args[0][0] == '/usr/bin/nmcli' + assert get_available_options_args[0][1] == 'con' + assert get_available_options_args[0][2] == 'edit' + assert get_available_options_args[0][3] == 'type' + assert get_available_options_args[0][4] == 'wifi' + + get_available_options_data = get_available_options_kw['data'].split() + for param in ['print', '802-11-wireless-security', + 'quit', 'yes']: + assert param in get_available_options_data + + 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] == 'add' + assert add_args[0][3] == 'type' + assert add_args[0][4] == 'wifi' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'wireless_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + '802-11-wireless.ssid', 'Brittany', + '802-11-wireless-security.key-mgmt', 'wpa-psk']: + assert param in add_args_text + + out, err = capfd.readouterr() + results = json.loads(out) + assert results.get('failed') + assert 'changed' not in results + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SECURE_WIRELESS, indirect=['patch_ansible_module']) +def test_modify_secure_wireless(mocked_secure_wireless_modify, capfd): + """ + Test : Modify secure wireless connection + """ + + with pytest.raises(SystemExit): + nmcli.main() + assert nmcli.Nmcli.execute_command.call_count == 4 + arg_list = nmcli.Nmcli.execute_command.call_args_list + + get_available_options_args, get_available_options_kw = arg_list[0] + assert get_available_options_args[0][0] == '/usr/bin/nmcli' + assert get_available_options_args[0][1] == 'con' + assert get_available_options_args[0][2] == 'edit' + assert get_available_options_args[0][3] == 'type' + assert get_available_options_args[0][4] == 'wifi' + + get_available_options_data = get_available_options_kw['data'].split() + for param in ['print', '802-11-wireless-security', + 'quit', 'yes']: + assert param in get_available_options_data + + show_args, show_kw = arg_list[1] + assert show_args[0][0] == '/usr/bin/nmcli' + assert show_args[0][1] == '--show-secrets' + assert show_args[0][2] == 'con' + assert show_args[0][3] == 'show' + assert show_args[0][4] == 'non_existent_nw_device' + + add_args, add_kw = arg_list[2] + 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 ['connection.interface-name', 'wireless_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + '802-11-wireless.ssid', 'Brittany', + '802-11-wireless-security.key-mgmt', 'wpa-psk']: + assert param in add_args_text + + edit_args, edit_kw = arg_list[3] + assert edit_args[0][0] == '/usr/bin/nmcli' + assert edit_args[0][1] == 'con' + assert edit_args[0][2] == 'edit' + assert edit_args[0][3] == 'non_existent_nw_device' + + edit_kw_data = edit_kw['data'].split() + for param in ['802-11-wireless-security.psk', 'VERY_SECURE_PASSWORD', + 'save', + 'quit']: + assert param in edit_kw_data + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SECURE_WIRELESS, indirect=['patch_ansible_module']) +def test_modify_secure_wireless_failure(mocked_secure_wireless_modify_failure, capfd): + """ + Test : Modify secure wireless connection w/failure + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 3 + arg_list = nmcli.Nmcli.execute_command.call_args_list + + get_available_options_args, get_available_options_kw = arg_list[0] + assert get_available_options_args[0][0] == '/usr/bin/nmcli' + assert get_available_options_args[0][1] == 'con' + assert get_available_options_args[0][2] == 'edit' + assert get_available_options_args[0][3] == 'type' + assert get_available_options_args[0][4] == 'wifi' + + get_available_options_data = get_available_options_kw['data'].split() + for param in ['print', '802-11-wireless-security', + 'quit', 'yes']: + assert param in get_available_options_data + + show_args, show_kw = arg_list[1] + assert show_args[0][0] == '/usr/bin/nmcli' + assert show_args[0][1] == '--show-secrets' + assert show_args[0][2] == 'con' + assert show_args[0][3] == 'show' + assert show_args[0][4] == 'non_existent_nw_device' + + add_args, add_kw = arg_list[2] + 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 ['connection.interface-name', 'wireless_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + '802-11-wireless.ssid', 'Brittany', + '802-11-wireless-security.key-mgmt', 'wpa-psk']: + assert param in add_args_text + + out, err = capfd.readouterr() + results = json.loads(out) + assert results.get('failed') + assert 'changed' not in results + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_DUMMY_STATIC, indirect=['patch_ansible_module']) +def test_create_dummy_static(mocked_generic_connection_create, capfd): + """ + Test : Create dummy connection with static IP configuration + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + 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] == 'dummy' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'dummy_non_existant', + 'ipv4.addresses', '10.10.10.10/24', + 'ipv4.gateway', '10.10.10.1', + 'ipv4.dns', '1.1.1.1,8.8.8.8', + 'ipv6.addresses', '2001:db8::1/128']: + assert param in add_args_text + + up_args, up_kw = arg_list[1] + assert up_args[0][0] == '/usr/bin/nmcli' + assert up_args[0][1] == 'con' + assert up_args[0][2] == 'up' + assert up_args[0][3] == 'non_existent_nw_device' + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_DUMMY_STATIC, indirect=['patch_ansible_module']) +def test_dummy_connection_static_unchanged(mocked_dummy_connection_static_unchanged, capfd): + """ + Test : Dummy connection with static IP configuration 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_DUMMY_STATIC, indirect=['patch_ansible_module']) +def test_dummy_connection_static_without_mtu_unchanged(mocked_dummy_connection_static_without_mtu_unchanged, capfd): + """ + Test : Dummy connection with static IP configuration and no mtu set 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_DUMMY_STATIC, indirect=['patch_ansible_module']) +def test_dummy_connection_static_with_custom_mtu_modify(mocked_dummy_connection_static_with_custom_mtu_modify, capfd): + """ + Test : Dummy connection with static IP configuration and no mtu set modify + """ + 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] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['802-3-ethernet.mtu', '0']: + assert param in 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_GSM, indirect=['patch_ansible_module']) +def test_create_gsm(mocked_generic_connection_create, capfd): + """ + Test if gsm 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] == 'gsm' + assert args[0][5] == 'con-name' + assert args[0][6] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['connection.interface-name', 'gsm_non_existant', + 'gsm.apn', 'internet.telekom', + 'gsm.username', 't-mobile', + 'gsm.password', 'tm', + 'gsm.pin', '1234']: + assert param in 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_GSM, indirect=['patch_ansible_module']) +def test_gsm_mod(mocked_generic_connection_modify, capfd): + """ + Test if gsm modified + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['gsm.username', 't-mobile', + 'gsm.password', 'tm']: + assert param in 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_GSM, indirect=['patch_ansible_module']) +def test_gsm_connection_unchanged(mocked_gsm_connection_unchanged, capfd): + """ + Test if gsm 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_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES, indirect=['patch_ansible_module']) +def test_create_ethernet_with_multiple_ip4_addresses_static(mocked_generic_connection_create, capfd): + """ + Test : Create ethernet connection with static IP configuration + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'ethernet_non_existant', + 'ipv4.addresses', '10.10.10.10/32,10.10.20.10/32', + 'ipv4.gateway', '10.10.10.1', + 'ipv4.dns', '1.1.1.1,8.8.8.8']: + assert param in add_args_text + + up_args, up_kw = arg_list[1] + assert up_args[0][0] == '/usr/bin/nmcli' + assert up_args[0][1] == 'con' + assert up_args[0][2] == 'up' + assert up_args[0][3] == 'non_existent_nw_device' + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC_MULTIPLE_IP6_ADDRESSES, indirect=['patch_ansible_module']) +def test_create_ethernet_with_multiple_ip6_addresses_static(mocked_generic_connection_create, capfd): + """ + Test : Create ethernet connection with multiple IPv6 addresses configuration + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'ethernet_non_existant', + 'ipv6.addresses', '2001:db8::cafe/128,2002:db8::cafe/128', + 'ipv6.gateway', '2001:db8::cafa', + 'ipv6.dns', '2001:4860:4860::8888,2001:4860:4860::8844']: + assert param in add_args_text + + up_args, up_kw = arg_list[1] + assert up_args[0][0] == '/usr/bin/nmcli' + assert up_args[0][1] == 'con' + assert up_args[0][2] == 'up' + assert up_args[0][3] == 'non_existent_nw_device' + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_with_multiple_ip4_addresses_unchanged(mocked_ethernet_connection_static_multiple_ip4_addresses_unchanged, capfd): + """ + Test : Ethernet connection with static IP configuration 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_ETHERNET_STATIC_MULTIPLE_IP6_ADDRESSES, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_with_multiple_ip6_addresses_unchanged(mocked_ethernet_connection_static_multiple_ip6_addresses_unchanged, capfd): + """ + Test : Ethernet connection with multiple IPv6 addresses configuration 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_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES, indirect=['patch_ansible_module']) +def test_add_second_ip4_address_to_ethernet_connection(mocked_ethernet_connection_static_modify, capfd): + """ + Test : Modify ethernet connection from DHCP to static + """ + 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] == 'non_existent_nw_device' + + for param in ['ipv4.addresses', '10.10.10.10/32,10.10.20.10/32']: + assert param 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_ETHERNET_STATIC_IP6_PRIVACY_AND_ADDR_GEN_MODE, indirect=['patch_ansible_module']) +def test_create_ethernet_addr_gen_mode_and_ip6_privacy_static(mocked_generic_connection_create, capfd): + """ + Test : Create ethernet connection with static IP configuration + """ + + with pytest.raises(SystemExit): + nmcli.main() + + assert nmcli.Nmcli.execute_command.call_count == 2 + 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] == 'ethernet' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'ethernet_non_existant', + 'ipv6.addresses', '2001:db8::cafe/128', + 'ipv6.gateway', '2001:db8::cafa', + 'ipv6.dns', '2001:4860:4860::8888', + 'ipv6.ip6-privacy', 'prefer-public-addr', + 'ipv6.addr-gen-mode', 'eui64']: + assert param in add_args_text + + up_args, up_kw = arg_list[1] + assert up_args[0][0] == '/usr/bin/nmcli' + assert up_args[0][1] == 'con' + assert up_args[0][2] == 'up' + assert up_args[0][3] == 'non_existent_nw_device' + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_STATIC_IP6_PRIVACY_AND_ADDR_GEN_MODE, indirect=['patch_ansible_module']) +def test_ethernet_connection_static_with_multiple_ip4_addresses_unchanged(mocked_ethernet_connection_static_ip6_privacy_and_addr_gen_mode_unchange, capfd): + """ + Test : Ethernet connection with static IP configuration 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_WIREGUARD, indirect=['patch_ansible_module']) +def test_create_wireguard(mocked_generic_connection_create, capfd): + """ + Test : Create wireguard connection with static IP configuration + """ + + 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] == 'wireguard' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'wg_non_existant', + 'ipv4.method', 'manual', + 'ipv4.addresses', '10.10.10.10/24', + 'ipv6.method', 'manual', + 'ipv6.addresses', '2001:db8::1/128', + 'wireguard.listen-port', '51820', + 'wireguard.private-key', '']: + 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_WIREGUARD, indirect=['patch_ansible_module']) +def test_wireguard_connection_unchanged(mocked_wireguard_connection_unchanged, capfd): + """ + Test : Wireguard connection with static IP configuration 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_WIREGUARD, indirect=['patch_ansible_module']) +def test_wireguard_mod(mocked_generic_connection_modify, capfd): + """ + Test : Modify wireguard connection + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['wireguard.listen-port', '51820']: + assert param in 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_VPN_L2TP, indirect=['patch_ansible_module']) +def test_vpn_l2tp_connection_unchanged(mocked_vpn_l2tp_connection_unchanged, capfd): + """ + Test : L2TP VPN 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_VPN_PPTP, indirect=['patch_ansible_module']) +def test_vpn_pptp_connection_unchanged(mocked_vpn_pptp_connection_unchanged, capfd): + """ + Test : PPTP VPN 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_VPN_L2TP, indirect=['patch_ansible_module']) +def test_create_vpn_l2tp(mocked_generic_connection_create, capfd): + """ + Test : Create L2TP VPN 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] == 'vpn' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'vpn_l2tp' + + add_args_text = list(map(to_text, add_args[0])) + + for param in ['connection.autoconnect', 'no', + 'connection.permissions', 'brittany', + 'vpn.data', 'vpn.service-type', 'org.freedesktop.NetworkManager.l2tp', + ]: + assert param in add_args_text + + vpn_data_index = add_args_text.index('vpn.data') + 1 + args_vpn_data = add_args_text[vpn_data_index] + for vpn_data in ['gateway=vpn.example.com', 'password-flags=2', 'user=brittany', 'ipsec-enabled=true', 'ipsec-psk=QnJpdHRhbnkxMjM=']: + assert vpn_data in args_vpn_data + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_VPN_PPTP, indirect=['patch_ansible_module']) +def test_create_vpn_pptp(mocked_generic_connection_create, capfd): + """ + Test : Create PPTP VPN 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] == 'vpn' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'vpn_pptp' + + add_args_text = list(map(to_text, add_args[0])) + + for param in ['connection.autoconnect', 'no', + 'connection.permissions', 'brittany', + 'vpn.data', 'vpn.service-type', 'org.freedesktop.NetworkManager.pptp', + ]: + assert param in add_args_text + + vpn_data_index = add_args_text.index('vpn.data') + 1 + args_vpn_data = add_args_text[vpn_data_index] + for vpn_data in ['password-flags=2', 'gateway=vpn.example.com', 'user=brittany']: + assert vpn_data in args_vpn_data + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get('failed') + assert results['changed'] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_INFINIBAND_STATIC, indirect=['patch_ansible_module']) +def test_infiniband_connection_static_unchanged(mocked_infiniband_connection_static_unchanged, capfd): + """ + Test : Infiniband 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_INFINIBAND_STATIC_MODIFY_TRANSPORT_MODE, indirect=['patch_ansible_module']) +def test_infiniband_connection_static_transport_mode_connected( + mocked_infiniband_connection_static_transport_mode_connected_modify, capfd): + """ + Test : Modify Infiniband connection to use connected as transport_mode + """ + 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 ['infiniband.transport-mode', 'connected']: + assert param in add_args_text + + out, err = capfd.readouterr() + results = json.loads(out) + + assert results.get('changed') is True + assert not results.get('failed') + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DIFF_CHECK, indirect=['patch_ansible_module']) +def test_bond_connection_unchanged(mocked_generic_connection_diff_check, capfd): + """ + Test : Bond connection unchanged + """ + + module = AnsibleModule( + argument_spec=dict( + ignore_unsupported_suboptions=dict(type='bool', default=False), + autoconnect=dict(type='bool', default=True), + state=dict(type='str', required=True, choices=['absent', 'present']), + conn_name=dict(type='str', required=True), + master=dict(type='str'), + ifname=dict(type='str'), + type=dict(type='str', + choices=[ + 'bond', + 'bond-slave', + 'bridge', + 'bridge-slave', + 'dummy', + 'ethernet', + 'generic', + 'gre', + 'infiniband', + 'ipip', + 'sit', + 'team', + 'team-slave', + 'vlan', + 'vxlan', + 'wifi', + 'gsm', + 'macvlan', + 'wireguard', + 'vpn', + ]), + ip4=dict(type='list', elements='str'), + gw4=dict(type='str'), + gw4_ignore_auto=dict(type='bool', default=False), + routes4=dict(type='list', elements='str'), + routes4_extended=dict(type='list', + elements='dict', + options=dict( + ip=dict(type='str', required=True), + next_hop=dict(type='str'), + metric=dict(type='int'), + table=dict(type='int'), + tos=dict(type='int'), + cwnd=dict(type='int'), + mtu=dict(type='int'), + onlink=dict(type='bool') + )), + route_metric4=dict(type='int'), + routing_rules4=dict(type='list', elements='str'), + never_default4=dict(type='bool', default=False), + dns4=dict(type='list', elements='str'), + dns4_search=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), + dhcp_client_id=dict(type='str'), + ip6=dict(type='list', elements='str'), + gw6=dict(type='str'), + gw6_ignore_auto=dict(type='bool', default=False), + dns6=dict(type='list', elements='str'), + dns6_search=dict(type='list', elements='str'), + dns6_ignore_auto=dict(type='bool', default=False), + routes6=dict(type='list', elements='str'), + routes6_extended=dict(type='list', + elements='dict', + options=dict( + ip=dict(type='str', required=True), + next_hop=dict(type='str'), + metric=dict(type='int'), + table=dict(type='int'), + cwnd=dict(type='int'), + mtu=dict(type='int'), + onlink=dict(type='bool') + )), + route_metric6=dict(type='int'), + method6=dict(type='str', choices=['ignore', 'auto', 'dhcp', 'link-local', 'manual', 'shared', 'disabled']), + ip_privacy6=dict(type='str', choices=['disabled', 'prefer-public-addr', 'prefer-temp-addr', 'unknown']), + addr_gen_mode6=dict(type='str', choices=['default', 'default-or-eui64', 'eui64', 'stable-privacy']), + # Bond Specific vars + mode=dict(type='str', default='balance-rr', + choices=['802.3ad', 'active-backup', 'balance-alb', 'balance-rr', 'balance-tlb', 'balance-xor', 'broadcast']), + miimon=dict(type='int'), + downdelay=dict(type='int'), + updelay=dict(type='int'), + xmit_hash_policy=dict(type='str'), + arp_interval=dict(type='int'), + arp_ip_target=dict(type='str'), + primary=dict(type='str'), + # general usage + mtu=dict(type='int'), + mac=dict(type='str'), + zone=dict(type='str'), + # bridge specific vars + stp=dict(type='bool', default=True), + priority=dict(type='int', default=128), + slavepriority=dict(type='int', default=32), + forwarddelay=dict(type='int', default=15), + hellotime=dict(type='int', default=2), + maxage=dict(type='int', default=20), + ageingtime=dict(type='int', default=300), + hairpin=dict(type='bool'), + path_cost=dict(type='int', default=100), + # team specific vars + runner=dict(type='str', default='roundrobin', + choices=['broadcast', 'roundrobin', 'activebackup', 'loadbalance', 'lacp']), + # team active-backup runner specific options + runner_hwaddr_policy=dict(type='str', choices=['same_all', 'by_active', 'only_active']), + # team lacp runner specific options + runner_fast_rate=dict(type='bool'), + # vlan specific vars + vlanid=dict(type='int'), + vlandev=dict(type='str'), + flags=dict(type='str'), + ingress=dict(type='str'), + egress=dict(type='str'), + # vxlan specific vars + vxlan_id=dict(type='int'), + vxlan_local=dict(type='str'), + vxlan_remote=dict(type='str'), + # ip-tunnel specific vars + ip_tunnel_dev=dict(type='str'), + ip_tunnel_local=dict(type='str'), + ip_tunnel_remote=dict(type='str'), + # ip-tunnel type gre specific vars + ip_tunnel_input_key=dict(type='str', no_log=True), + ip_tunnel_output_key=dict(type='str', no_log=True), + # 802-11-wireless* specific vars + ssid=dict(type='str'), + wifi=dict(type='dict'), + wifi_sec=dict(type='dict', no_log=True), + gsm=dict(type='dict'), + macvlan=dict(type='dict'), + wireguard=dict(type='dict'), + vpn=dict(type='dict'), + transport_mode=dict(type='str', choices=['datagram', 'connected']), + ), + mutually_exclusive=[['never_default4', 'gw4'], + ['routes4_extended', 'routes4'], + ['routes6_extended', 'routes6']], + required_if=[("type", "wifi", [("ssid")])], + supports_check_mode=True, + ) + module.run_command_environ_update = dict(LANG='C', LC_ALL='C', LC_MESSAGES='C', LC_CTYPE='C') + + nmcli_module = nmcli.Nmcli(module) + + changed, diff = nmcli_module.is_connection_changed() + + assert changed + + num_of_diff_params = 0 + for parameter, value in diff.get('before').items(): + if value != diff['after'][parameter]: + num_of_diff_params += 1 + + assert num_of_diff_params == 1 + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_MACVLAN, indirect=['patch_ansible_module']) +def test_create_macvlan(mocked_generic_connection_create, capfd): + """ + Test : Create macvlan connection with static IP configuration + """ + + 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] == 'macvlan' + assert add_args[0][5] == 'con-name' + assert add_args[0][6] == 'non_existent_nw_device' + + add_args_text = list(map(to_text, add_args[0])) + for param in ['connection.interface-name', 'macvlan_non_existant', + 'ipv4.method', 'manual', + 'ipv4.addresses', '10.10.10.10/24', + 'ipv6.method', 'manual', + 'ipv6.addresses', '2001:db8::1/128', + 'macvlan.mode', '2', + 'macvlan.parent', 'non_existent_parent']: + 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_MACVLAN, indirect=['patch_ansible_module']) +def test_macvlan_connection_unchanged(mocked_macvlan_connection_unchanged, capfd): + """ + Test : Macvlan connection with static IP configuration 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_MACVLAN, indirect=['patch_ansible_module']) +def test_macvlan_mod(mocked_generic_connection_modify, capfd): + """ + Test : Modify macvlan connection + """ + 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] == 'modify' + assert args[0][3] == 'non_existent_nw_device' + + args_text = list(map(to_text, args[0])) + for param in ['macvlan.mode', '2']: + assert param in args_text + + 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_npm.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py new file mode 100644 index 000000000..f5d312775 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py @@ -0,0 +1,262 @@ +# +# Copyright (c) 2021, Abhijeet Kasurde +# 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 call, patch +from ansible_collections.community.general.plugins.modules import npm +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, ModuleTestCase, set_module_args + + +class NPMModuleTestCase(ModuleTestCase): + module = npm + + def setUp(self): + super(NPMModuleTestCase, self).setUp() + ansible_module_path = "ansible_collections.community.general.plugins.modules.npm.AnsibleModule" + self.mock_run_command = patch('%s.run_command' % ansible_module_path) + self.module_main_command = self.mock_run_command.start() + self.mock_get_bin_path = patch('%s.get_bin_path' % ansible_module_path) + self.get_bin_path = self.mock_get_bin_path.start() + self.get_bin_path.return_value = '/testbin/npm' + + def tearDown(self): + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + super(NPMModuleTestCase, self).tearDown() + + def module_main(self, exit_exc): + with self.assertRaises(exit_exc) as exc: + self.module.main() + return exc.exception.args[0] + + def test_present(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'present' + }) + self.module_main_command.side_effect = [ + (0, '{}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_present_missing(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'present', + }) + self.module_main_command.side_effect = [ + (0, '{"dependencies": {"coffee-script": {"missing" : true}}}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_present_version(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'present', + 'version': '2.5.1' + }) + self.module_main_command.side_effect = [ + (0, '{}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_present_version_update(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'present', + 'version': '2.5.1' + }) + self.module_main_command.side_effect = [ + (0, '{"dependencies": {"coffee-script": {"version" : "2.5.0"}}}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_present_version_exists(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'present', + 'version': '2.5.1' + }) + self.module_main_command.side_effect = [ + (0, '{"dependencies": {"coffee-script": {"version" : "2.5.1"}}}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertFalse(result['changed']) + self.module_main_command.assert_has_calls([ + call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None), + ]) + + def test_absent(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'absent' + }) + self.module_main_command.side_effect = [ + (0, '{"dependencies": {"coffee-script": {"version" : "2.5.1"}}}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_absent_version(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'absent', + 'version': '2.5.1' + }) + self.module_main_command.side_effect = [ + (0, '{"dependencies": {"coffee-script": {"version" : "2.5.1"}}}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_absent_version_different(self): + set_module_args({ + 'name': 'coffee-script', + 'global': 'true', + 'state': 'absent', + 'version': '2.5.1' + }) + self.module_main_command.side_effect = [ + (0, '{"dependencies": {"coffee-script": {"version" : "2.5.0"}}}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_present_package_json(self): + set_module_args({ + 'global': 'true', + 'state': 'present' + }) + self.module_main_command.side_effect = [ + (0, '{}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.module_main_command.assert_has_calls([ + call(['/testbin/npm', 'install', '--global'], check_rc=True, cwd=None), + ]) + + def test_present_package_json_production(self): + set_module_args({ + 'production': 'true', + 'global': 'true', + 'state': 'present', + }) + self.module_main_command.side_effect = [ + (0, '{}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.module_main_command.assert_has_calls([ + call(['/testbin/npm', 'install', '--global', '--production'], check_rc=True, cwd=None), + ]) + + def test_present_package_json_ci(self): + set_module_args({ + 'ci': 'true', + 'global': 'true', + 'state': 'present' + }) + self.module_main_command.side_effect = [ + (0, '{}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.module_main_command.assert_has_calls([ + call(['/testbin/npm', 'ci', '--global'], check_rc=True, cwd=None), + ]) + + def test_present_package_json_ci_production(self): + set_module_args({ + 'ci': 'true', + 'production': 'true', + 'global': 'true', + 'state': 'present' + }) + self.module_main_command.side_effect = [ + (0, '{}', ''), + (0, '{}', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.module_main_command.assert_has_calls([ + call(['/testbin/npm', 'ci', '--global', '--production'], check_rc=True, cwd=None), + ]) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_command.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_command.py new file mode 100644 index 000000000..3ce267c4e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_command.py @@ -0,0 +1,639 @@ +# -*- 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 os +import shutil +import tempfile + +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible.module_utils import basic +import ansible_collections.community.general.plugins.modules.ocapi_command as module +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args, exit_json, fail_json +from ansible.module_utils.six.moves.urllib.parse import urljoin + + +MOCK_BASE_URI = "mockBaseUri/" +OPERATING_SYSTEM_URI = "OperatingSystem" +MOCK_JOB_NAME = "MockJob" + +ACTION_WAS_SUCCESSFUL = "Action was successful." +UPDATE_NOT_PERFORMED_IN_CHECK_MODE = "Update not performed in check mode." +NO_ACTION_PERFORMED_IN_CHECK_MODE = "No action performed in check mode." + +MOCK_SUCCESSFUL_HTTP_RESPONSE_LED_INDICATOR_OFF_WITH_ETAG = { + "ret": True, + "data": { + "IndicatorLED": { + "ID": 4, + "Name": "Off" + }, + "PowerState": { + "ID": 2, + "Name": "On" + } + }, + "headers": {"etag": "MockETag"} +} + +MOCK_SUCCESSFUL_HTTP_RESPONSE = { + "ret": True, + "data": {} +} + +MOCK_404_RESPONSE = { + "ret": False, + "status": 404 +} + +MOCK_SUCCESSFUL_HTTP_RESPONSE_WITH_LOCATION_HEADER = { + "ret": True, + "data": {}, + "headers": {"location": "mock_location"} +} + +MOCK_HTTP_RESPONSE_CONFLICT = { + "ret": False, + "msg": "Conflict", + "status": 409 +} + +MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS = { + "ret": True, + "data": { + "PercentComplete": 99 + }, + "headers": { + "etag": "12345" + } +} + +MOCK_HTTP_RESPONSE_JOB_COMPLETE = { + "ret": True, + "data": { + "PercentComplete": 100 + }, + "headers": { + "etag": "12345" + } +} + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + return arg + + +def get_exception_message(ansible_exit_json): + """From an AnsibleExitJson exception, get the message string.""" + return ansible_exit_json.exception.args[0]["msg"] + + +def is_changed(ansible_exit_json): + """From an AnsibleExitJson exception, return the value of the changed flag""" + return ansible_exit_json.exception.args[0]["changed"] + + +def mock_get_request(*args, **kwargs): + """Mock for get_request.""" + url = args[1] + if url == 'https://' + MOCK_BASE_URI: + return MOCK_SUCCESSFUL_HTTP_RESPONSE_LED_INDICATOR_OFF_WITH_ETAG + elif url == "mock_location": + return MOCK_SUCCESSFUL_HTTP_RESPONSE + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_get_request_job_does_not_exist(*args, **kwargs): + """Mock for get_request.""" + url = args[1] + if url == 'https://' + MOCK_BASE_URI: + return MOCK_SUCCESSFUL_HTTP_RESPONSE_LED_INDICATOR_OFF_WITH_ETAG + elif url == urljoin('https://' + MOCK_BASE_URI, "Jobs/" + MOCK_JOB_NAME): + return MOCK_404_RESPONSE + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_get_request_job_in_progress(*args, **kwargs): + url = args[1] + if url == 'https://' + MOCK_BASE_URI: + return MOCK_SUCCESSFUL_HTTP_RESPONSE_LED_INDICATOR_OFF_WITH_ETAG + elif url == urljoin('https://' + MOCK_BASE_URI, "Jobs/" + MOCK_JOB_NAME): + return MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_get_request_job_complete(*args, **kwargs): + url = args[1] + if url == 'https://' + MOCK_BASE_URI: + return MOCK_SUCCESSFUL_HTTP_RESPONSE_LED_INDICATOR_OFF_WITH_ETAG + elif url == urljoin('https://' + MOCK_BASE_URI, "Jobs/" + MOCK_JOB_NAME): + return MOCK_HTTP_RESPONSE_JOB_COMPLETE + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_put_request(*args, **kwargs): + """Mock put_request.""" + url = args[1] + if url == 'https://' + MOCK_BASE_URI: + return MOCK_SUCCESSFUL_HTTP_RESPONSE_WITH_LOCATION_HEADER + raise RuntimeError("Illegal PUT call to: " + args[1]) + + +def mock_delete_request(*args, **kwargs): + """Mock delete request.""" + url = args[1] + if url == urljoin('https://' + MOCK_BASE_URI, 'Jobs/' + MOCK_JOB_NAME): + return MOCK_SUCCESSFUL_HTTP_RESPONSE + raise RuntimeError("Illegal DELETE call to: " + args[1]) + + +def mock_post_request(*args, **kwargs): + """Mock post_request.""" + url = args[1] + if url == urljoin('https://' + MOCK_BASE_URI, OPERATING_SYSTEM_URI): + return MOCK_SUCCESSFUL_HTTP_RESPONSE + raise RuntimeError("Illegal POST call to: " + args[1]) + + +def mock_http_request_conflict(*args, **kwargs): + """Mock to make an HTTP request return 409 Conflict""" + return MOCK_HTTP_RESPONSE_CONFLICT + + +def mock_invalid_http_request(*args, **kwargs): + """Mock to make an HTTP request invalid. Raises an exception.""" + raise RuntimeError("Illegal HTTP call to " + args[1]) + + +class TestOcapiCommand(unittest.TestCase): + + def setUp(self): + self.mock_module_helper = 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.tempdir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({}) + module.main() + self.assertIn("missing required arguments:", get_exception_message(ansible_fail_json)) + + def test_module_fail_when_unknown_category(self): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'unknown', + 'command': 'IndicatorLedOn', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'baseuri': MOCK_BASE_URI + }) + module.main() + self.assertIn("Invalid Category 'unknown", get_exception_message(ansible_fail_json)) + + def test_set_power_mode(self): + """Test that we can set chassis power mode""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'PowerModeLow', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_set_chassis_led_indicator(self): + """Test that we can set chassis LED indicator.""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'IndicatorLedOn', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_set_power_mode_already_set(self): + """Test that if we set Power Mode to normal when it's already normal, we get changed=False.""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'PowerModeNormal', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertFalse(is_changed(ansible_exit_json)) + + def test_set_power_mode_check_mode(self): + """Test check mode when setting chassis Power Mode.""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'IndicatorLedOn', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + '_ansible_check_mode': True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_set_chassis_led_indicator_check_mode(self): + """Test check mode when setting chassis LED indicator""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'IndicatorLedOn', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + '_ansible_check_mode': True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_set_chassis_led_indicator_already_set(self): + """Test that if we set LED Indicator to off when it's already off, we get changed=False.""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'IndicatorLedOff', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertFalse(is_changed(ansible_exit_json)) + + def test_set_chassis_led_indicator_already_set_check_mode(self): + """Test that if we set LED Indicator to off when it's already off, we get changed=False even in check mode.""" + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'IndicatorLedOff', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + "_ansible_check_mode": True + }) + module.main() + self.assertEqual(NO_ACTION_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertFalse(is_changed(ansible_exit_json)) + + def test_set_chassis_invalid_indicator_command(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Chassis', + 'command': 'IndicatorLedBright', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertIn("Invalid Command", get_exception_message(ansible_fail_json)) + + def test_reset_enclosure(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Systems', + 'command': 'PowerGracefulRestart', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_reset_enclosure_check_mode(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Systems', + 'command': 'PowerGracefulRestart', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + "_ansible_check_mode": True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_firmware_upload_missing_update_image_path(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWUpload', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual("Missing update_image_path.", get_exception_message(ansible_fail_json)) + + def test_firmware_upload_file_not_found(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWUpload', + 'update_image_path': 'nonexistentfile.bin', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual("File does not exist.", get_exception_message(ansible_fail_json)) + + def test_firmware_upload(self): + filename = "fake_firmware.bin" + filepath = os.path.join(self.tempdir, filename) + file_contents = b'\x00\x01\x02\x03\x04' + with open(filepath, 'wb+') as f: + f.write(file_contents) + + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWUpload', + 'update_image_path': filepath, + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_firmware_upload_check_mode(self): + filename = "fake_firmware.bin" + filepath = os.path.join(self.tempdir, filename) + file_contents = b'\x00\x01\x02\x03\x04' + with open(filepath, 'wb+') as f: + f.write(file_contents) + + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWUpload', + 'update_image_path': filepath, + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + "_ansible_check_mode": True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_firmware_update(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWUpdate', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_firmware_update_check_mode(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWUpdate', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + "_ansible_check_mode": True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_firmware_activate(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWActivate', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_firmware_activate_check_mode(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Update', + 'command': 'FWActivate', + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21', + "_ansible_check_mode": True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_delete_job(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request_job_complete, + delete_request=mock_delete_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'DeleteJob', + 'baseuri': MOCK_BASE_URI, + 'job_name': MOCK_JOB_NAME, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_delete_job_in_progress(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request_job_in_progress, + delete_request=mock_invalid_http_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'DeleteJob', + 'baseuri': MOCK_BASE_URI, + 'job_name': MOCK_JOB_NAME, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual("Cannot delete job because it is in progress.", get_exception_message(ansible_fail_json)) + + def test_delete_job_in_progress_only_on_delete(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request_job_complete, + delete_request=mock_http_request_conflict, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'DeleteJob', + 'baseuri': MOCK_BASE_URI, + 'job_name': MOCK_JOB_NAME, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual("Cannot delete job because it is in progress.", get_exception_message(ansible_fail_json)) + + def test_delete_job_check_mode(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request_job_complete, + delete_request=mock_delete_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'DeleteJob', + 'baseuri': MOCK_BASE_URI, + 'job_name': MOCK_JOB_NAME, + 'username': 'USERID', + 'password': 'PASSWORD=21', + '_ansible_check_mode': True + }) + module.main() + self.assertEqual(UPDATE_NOT_PERFORMED_IN_CHECK_MODE, get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_delete_job_check_mode_job_not_found(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request_job_does_not_exist, + delete_request=mock_delete_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'DeleteJob', + 'baseuri': MOCK_BASE_URI, + 'job_name': MOCK_JOB_NAME, + 'username': 'USERID', + 'password': 'PASSWORD=21', + '_ansible_check_mode': True + }) + module.main() + self.assertEqual("Job already deleted.", get_exception_message(ansible_exit_json)) + self.assertFalse(is_changed(ansible_exit_json)) + + def test_delete_job_check_mode_job_in_progress(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request_job_in_progress, + delete_request=mock_delete_request, + put_request=mock_invalid_http_request, + post_request=mock_invalid_http_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'DeleteJob', + 'baseuri': MOCK_BASE_URI, + 'job_name': MOCK_JOB_NAME, + 'username': 'USERID', + 'password': 'PASSWORD=21', + '_ansible_check_mode': True + }) + module.main() + self.assertEqual("Cannot delete job because it is in progress.", get_exception_message(ansible_fail_json)) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_info.py new file mode 100644 index 000000000..5010b328f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ocapi_info.py @@ -0,0 +1,240 @@ +# -*- 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 + +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible.module_utils import basic +import ansible_collections.community.general.plugins.modules.ocapi_info as module +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args, exit_json, fail_json + +MOCK_BASE_URI = "mockBaseUri" +MOCK_JOB_NAME_IN_PROGRESS = "MockJobInProgress" +MOCK_JOB_NAME_COMPLETE = "MockJobComplete" +MOCK_JOB_NAME_DOES_NOT_EXIST = "MockJobDoesNotExist" + +ACTION_WAS_SUCCESSFUL = "Action was successful." + +MOCK_SUCCESSFUL_HTTP_RESPONSE = { + "ret": True, + "data": {} +} + +MOCK_404_RESPONSE = { + "ret": False, + "status": 404 +} + +MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS = { + "ret": True, + "data": { + "Self": "https://openflex-data24-usalp02120qo0012-iomb:443/Storage/Devices/openflex-data24-usalp02120qo0012/Jobs/FirmwareUpdate/", + "ID": MOCK_JOB_NAME_IN_PROGRESS, + "PercentComplete": 10, + "Status": { + "State": { + "ID": 16, + "Name": "In service" + }, + "Health": [ + { + "ID": 5, + "Name": "OK" + } + ] + } + } +} + +MOCK_HTTP_RESPONSE_JOB_COMPLETE = { + "ret": True, + "data": { + "Self": "https://openflex-data24-usalp02120qo0012-iomb:443/Storage/Devices/openflex-data24-usalp02120qo0012/Jobs/FirmwareUpdate/", + "ID": MOCK_JOB_NAME_COMPLETE, + "PercentComplete": 100, + "Status": { + "State": { + "ID": 65540, + "Name": "Activate needed" + }, + "Health": [ + { + "ID": 5, + "Name": "OK" + } + ], + "Details": [ + "Completed." + ] + } + } +} + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + return arg + + +def get_exception_message(ansible_exit_json): + """From an AnsibleExitJson exception, get the message string.""" + return ansible_exit_json.exception.args[0]["msg"] + + +def mock_get_request(*args, **kwargs): + """Mock for get_request.""" + url = args[1] + if url == "https://" + MOCK_BASE_URI: + return MOCK_SUCCESSFUL_HTTP_RESPONSE + elif url == "https://" + MOCK_BASE_URI + '/Jobs/' + MOCK_JOB_NAME_IN_PROGRESS: + return MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS + elif url == "https://" + MOCK_BASE_URI + '/Jobs/' + MOCK_JOB_NAME_COMPLETE: + return MOCK_HTTP_RESPONSE_JOB_COMPLETE + elif url == "https://" + MOCK_BASE_URI + '/Jobs/' + MOCK_JOB_NAME_DOES_NOT_EXIST: + return MOCK_404_RESPONSE + else: + raise RuntimeError("Illegal GET call to: " + args[1]) + + +def mock_put_request(*args, **kwargs): + """Mock put_request. PUT should never happen so it will raise an error.""" + raise RuntimeError("Illegal PUT call to: " + args[1]) + + +def mock_delete_request(*args, **kwargs): + """Mock delete request. DELETE should never happen so it will raise an error.""" + raise RuntimeError("Illegal DELETE call to: " + args[1]) + + +def mock_post_request(*args, **kwargs): + """Mock post_request. POST should never happen so it will raise an error.""" + raise RuntimeError("Illegal POST call to: " + args[1]) + + +class TestOcapiInfo(unittest.TestCase): + def setUp(self): + self.mock_module_helper = 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) + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({}) + module.main() + self.assertIn("missing required arguments:", get_exception_message(ansible_fail_json)) + + def test_module_fail_when_unknown_category(self): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'unknown', + 'command': 'JobStatus', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'baseuri': MOCK_BASE_URI + }) + module.main() + self.assertIn("Invalid Category 'unknown", get_exception_message(ansible_fail_json)) + + def test_module_fail_when_unknown_command(self): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'unknown', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'baseuri': MOCK_BASE_URI + }) + module.main() + self.assertIn("Invalid Command 'unknown", get_exception_message(ansible_fail_json)) + + def test_job_status_in_progress(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + delete_request=mock_delete_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'JobStatus', + 'job_name': MOCK_JOB_NAME_IN_PROGRESS, + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + response_data = ansible_exit_json.exception.args[0] + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS["data"]["PercentComplete"], response_data["percentComplete"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS["data"]["Status"]["State"]["ID"], response_data["operationStatusId"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS["data"]["Status"]["State"]["Name"], response_data["operationStatus"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS["data"]["Status"]["Health"][0]["Name"], response_data["operationHealth"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_IN_PROGRESS["data"]["Status"]["Health"][0]["ID"], response_data["operationHealthId"]) + self.assertTrue(response_data["jobExists"]) + self.assertFalse(response_data["changed"]) + self.assertEqual(ACTION_WAS_SUCCESSFUL, response_data["msg"]) + self.assertIsNone(response_data["details"]) + + def test_job_status_complete(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + delete_request=mock_delete_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'JobStatus', + 'job_name': MOCK_JOB_NAME_COMPLETE, + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + response_data = ansible_exit_json.exception.args[0] + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_COMPLETE["data"]["PercentComplete"], response_data["percentComplete"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_COMPLETE["data"]["Status"]["State"]["ID"], response_data["operationStatusId"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_COMPLETE["data"]["Status"]["State"]["Name"], response_data["operationStatus"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_COMPLETE["data"]["Status"]["Health"][0]["Name"], response_data["operationHealth"]) + self.assertEqual(MOCK_HTTP_RESPONSE_JOB_COMPLETE["data"]["Status"]["Health"][0]["ID"], response_data["operationHealthId"]) + self.assertTrue(response_data["jobExists"]) + self.assertFalse(response_data["changed"]) + self.assertEqual(ACTION_WAS_SUCCESSFUL, response_data["msg"]) + self.assertEqual(["Completed."], response_data["details"]) + + def test_job_status_not_found(self): + with patch.multiple("ansible_collections.community.general.plugins.module_utils.ocapi_utils.OcapiUtils", + get_request=mock_get_request, + put_request=mock_put_request, + delete_request=mock_delete_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + set_module_args({ + 'category': 'Jobs', + 'command': 'JobStatus', + 'job_name': MOCK_JOB_NAME_DOES_NOT_EXIST, + 'baseuri': MOCK_BASE_URI, + 'username': 'USERID', + 'password': 'PASSWORD=21' + }) + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL, get_exception_message(ansible_exit_json)) + response_data = ansible_exit_json.exception.args[0] + self.assertFalse(response_data["jobExists"]) + self.assertEqual(0, response_data["percentComplete"]) + self.assertEqual(1, response_data["operationStatusId"]) + self.assertEqual("Not Available", response_data["operationStatus"]) + self.assertIsNone(response_data["operationHealth"]) + self.assertIsNone(response_data["operationHealthId"]) + self.assertFalse(response_data["changed"]) + self.assertEqual(ACTION_WAS_SUCCESSFUL, response_data["msg"]) + self.assertEqual("Job does not exist.", response_data["details"]) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_one_vm.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_one_vm.py new file mode 100644 index 000000000..fcfa685af --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_one_vm.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2023, Michal Opala +# 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 + +from ansible_collections.community.general.plugins.modules.one_vm import parse_updateconf + + +PARSE_UPDATECONF_VALID = [ + ( + { + "CPU": 1, + "OS": {"ARCH": 2}, + }, + { + "OS": {"ARCH": 2}, + } + ), + ( + { + "OS": {"ARCH": 1, "ASD": 2}, # "ASD" is an invalid attribute, we ignore it + }, + { + "OS": {"ARCH": 1}, + } + ), + ( + { + "OS": {"ASD": 1}, # "ASD" is an invalid attribute, we ignore it + }, + { + } + ), + ( + { + "MEMORY": 1, + "CONTEXT": { + "PASSWORD": 2, + "SSH_PUBLIC_KEY": 3, + }, + }, + { + "CONTEXT": { + "PASSWORD": 2, + "SSH_PUBLIC_KEY": 3, + }, + } + ), +] + + +@pytest.mark.parametrize('vm_template,expected_result', PARSE_UPDATECONF_VALID) +def test_parse_updateconf(vm_template, expected_result): + result = parse_updateconf(vm_template) + assert result == expected_result, repr(result) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_datacenter_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_datacenter_info.py new file mode 100644 index 000000000..eb5e05d22 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_datacenter_info.py @@ -0,0 +1,80 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 + +from .hpe_test_utils import FactsParamsTest +from .oneview_conftest import mock_ov_client, mock_ansible_module # noqa: F401, pylint: disable=unused-import + +from ansible_collections.community.general.plugins.modules.oneview_datacenter_info import DatacenterInfoModule + +PARAMS_GET_CONNECTED = dict( + config='config.json', + name="MyDatacenter", + options=['visualContent'] +) + + +class TestDatacenterInfoModule(FactsParamsTest): + @pytest.fixture(autouse=True) + def setUp(self, mock_ansible_module, mock_ov_client): + self.resource = mock_ov_client.datacenters + self.mock_ansible_module = mock_ansible_module + self.mock_ov_client = mock_ov_client + + def test_should_get_all_datacenters(self): + self.resource.get_all.return_value = {"name": "Data Center Name"} + + self.mock_ansible_module.params = dict(config='config.json',) + + DatacenterInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + datacenters=({"name": "Data Center Name"}) + ) + + def test_should_get_datacenter_by_name(self): + self.resource.get_by.return_value = [{"name": "Data Center Name"}] + + self.mock_ansible_module.params = dict(config='config.json', name="MyDatacenter") + + DatacenterInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + datacenters=([{"name": "Data Center Name"}]) + ) + + def test_should_get_datacenter_visual_content(self): + self.resource.get_by.return_value = [{"name": "Data Center Name", "uri": "/rest/datacenter/id"}] + + self.resource.get_visual_content.return_value = { + "name": "Visual Content"} + + self.mock_ansible_module.params = PARAMS_GET_CONNECTED + + DatacenterInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + datacenter_visual_content={'name': 'Visual Content'}, + datacenters=[{'name': 'Data Center Name', 'uri': '/rest/datacenter/id'}] + ) + + def test_should_get_none_datacenter_visual_content(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_GET_CONNECTED + + DatacenterInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + datacenter_visual_content=None, + datacenters=[] + ) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_enclosure_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_enclosure_info.py new file mode 100644 index 000000000..e8ef3449f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_enclosure_info.py @@ -0,0 +1,137 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .hpe_test_utils import FactsParamsTestCase + +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible_collections.community.general.plugins.modules.oneview_enclosure_info import EnclosureInfoModule + + +ERROR_MSG = 'Fake message error' + +PARAMS_GET_ALL = dict( + config='config.json', + name=None +) + +PARAMS_GET_BY_NAME = dict( + config='config.json', + name="Test-Enclosure", + options=[] +) + +PARAMS_GET_BY_NAME_WITH_OPTIONS = dict( + config='config.json', + name="Test-Enclosure", + options=['utilization', 'environmentalConfiguration', 'script'] +) + +PARAMS_GET_UTILIZATION_WITH_PARAMS = dict( + config='config.json', + name="Test-Enclosure", + options=[dict(utilization=dict(fields='AveragePower', + filter=['startDate=2016-06-30T03:29:42.000Z', + 'endDate=2016-07-01T03:29:42.000Z'], + view='day', + refresh=True))] +) + +PRESENT_ENCLOSURES = [{ + "name": "Test-Enclosure", + "uri": "/rest/enclosures/c6bf9af9-48e7-4236-b08a-77684dc258a5" +}] + +ENCLOSURE_SCRIPT = '# script content' + +ENCLOSURE_UTILIZATION = { + "isFresh": "True" +} + +ENCLOSURE_ENVIRONMENTAL_CONFIG = { + "calibratedMaxPower": "2500" +} + + +class EnclosureInfoSpec(unittest.TestCase, + FactsParamsTestCase): + def setUp(self): + self.configure_mocks(self, EnclosureInfoModule) + self.enclosures = self.mock_ov_client.enclosures + FactsParamsTestCase.configure_client_mock(self, self.enclosures) + + def test_should_get_all_enclosures(self): + self.enclosures.get_all.return_value = PRESENT_ENCLOSURES + self.mock_ansible_module.params = PARAMS_GET_ALL + + EnclosureInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + enclosures=(PRESENT_ENCLOSURES) + ) + + def test_should_get_enclosure_by_name(self): + self.enclosures.get_by.return_value = PRESENT_ENCLOSURES + self.mock_ansible_module.params = PARAMS_GET_BY_NAME + + EnclosureInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + enclosures=(PRESENT_ENCLOSURES) + + ) + + def test_should_get_enclosure_by_name_with_options(self): + self.enclosures.get_by.return_value = PRESENT_ENCLOSURES + self.enclosures.get_script.return_value = ENCLOSURE_SCRIPT + self.enclosures.get_utilization.return_value = ENCLOSURE_UTILIZATION + self.enclosures.get_environmental_configuration.return_value = ENCLOSURE_ENVIRONMENTAL_CONFIG + + self.mock_ansible_module.params = PARAMS_GET_BY_NAME_WITH_OPTIONS + + EnclosureInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + enclosures=PRESENT_ENCLOSURES, + enclosure_script=ENCLOSURE_SCRIPT, + enclosure_environmental_configuration=ENCLOSURE_ENVIRONMENTAL_CONFIG, + enclosure_utilization=ENCLOSURE_UTILIZATION + ) + + def test_should_get_all_utilization_data(self): + self.enclosures.get_by.return_value = PRESENT_ENCLOSURES + self.enclosures.get_script.return_value = ENCLOSURE_SCRIPT + self.enclosures.get_utilization.return_value = ENCLOSURE_UTILIZATION + self.enclosures.get_environmental_configuration.return_value = ENCLOSURE_ENVIRONMENTAL_CONFIG + + self.mock_ansible_module.params = PARAMS_GET_BY_NAME_WITH_OPTIONS + + EnclosureInfoModule().run() + + self.enclosures.get_utilization.assert_called_once_with(PRESENT_ENCLOSURES[0]['uri'], fields='', filter='', + view='', refresh='') + + def test_should_get_utilization_with_parameters(self): + self.enclosures.get_by.return_value = PRESENT_ENCLOSURES + self.enclosures.get_script.return_value = ENCLOSURE_SCRIPT + self.enclosures.get_utilization.return_value = ENCLOSURE_UTILIZATION + self.enclosures.get_environmental_configuration.return_value = ENCLOSURE_ENVIRONMENTAL_CONFIG + + self.mock_ansible_module.params = PARAMS_GET_UTILIZATION_WITH_PARAMS + + EnclosureInfoModule().run() + + date_filter = ["startDate=2016-06-30T03:29:42.000Z", "endDate=2016-07-01T03:29:42.000Z"] + + self.enclosures.get_utilization.assert_called_once_with( + PRESENT_ENCLOSURES[0]['uri'], fields='AveragePower', filter=date_filter, view='day', refresh=True) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network.py new file mode 100644 index 000000000..f1398740e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network.py @@ -0,0 +1,392 @@ +# -*- coding: utf-8 -*- +# +# Copyright (2016-2017) Hewlett Packard Enterprise Development LP +# 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 yaml + +from ansible_collections.community.general.tests.unit.compat import unittest, mock +from .oneview_module_loader import EthernetNetworkModule +from .hpe_test_utils import OneViewBaseTestCase + +FAKE_MSG_ERROR = 'Fake message error' +DEFAULT_ETHERNET_NAME = 'Test Ethernet Network' +RENAMED_ETHERNET = 'Renamed Ethernet Network' + +DEFAULT_ENET_TEMPLATE = dict( + name=DEFAULT_ETHERNET_NAME, + vlanId=200, + ethernetNetworkType="Tagged", + purpose="General", + smartLink=False, + privateNetwork=False, + connectionTemplateUri=None +) + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_ETHERNET_NAME) +) + +PARAMS_TO_RENAME = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_ETHERNET_NAME, + newName=RENAMED_ETHERNET) +) + +YAML_PARAMS_WITH_CHANGES = """ + config: "config.json" + state: present + data: + name: 'Test Ethernet Network' + purpose: Management + connectionTemplateUri: ~ + bandwidth: + maximumBandwidth: 3000 + typicalBandwidth: 2000 +""" + +YAML_RESET_CONNECTION_TEMPLATE = """ + config: "{{ config }}" + state: default_bandwidth_reset + data: + name: 'network name' +""" + +PARAMS_FOR_SCOPES_SET = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_ETHERNET_NAME) +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_ETHERNET_NAME) +) + +PARAMS_FOR_BULK_CREATED = dict( + config='config.json', + state='present', + data=dict(namePrefix="TestNetwork", vlanIdRange="1-2,5,9-10") +) + +DEFAULT_BULK_ENET_TEMPLATE = [ + {'name': 'TestNetwork_1', 'vlanId': 1}, + {'name': 'TestNetwork_2', 'vlanId': 2}, + {'name': 'TestNetwork_5', 'vlanId': 5}, + {'name': 'TestNetwork_9', 'vlanId': 9}, + {'name': 'TestNetwork_10', 'vlanId': 10}, +] + +DICT_PARAMS_WITH_CHANGES = yaml.safe_load(YAML_PARAMS_WITH_CHANGES)["data"] + + +class EthernetNetworkModuleSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase provides the mocks used in this test case + """ + + def setUp(self): + self.configure_mocks(self, EthernetNetworkModule) + self.resource = self.mock_ov_client.ethernet_networks + + def test_should_create_new_ethernet_network(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_ENET_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_CREATED, + ansible_facts=dict(ethernet_network=DEFAULT_ENET_TEMPLATE) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=EthernetNetworkModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(ethernet_network=DEFAULT_ENET_TEMPLATE) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['purpose'] = 'Management' + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + self.mock_ov_client.connection_templates.get.return_value = {"uri": "uri"} + + self.mock_ansible_module.params = yaml.safe_load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=data_merged) + ) + + def test_update_when_only_bandwidth_has_modified_attributes(self): + self.resource.get_by.return_value = [DICT_PARAMS_WITH_CHANGES] + self.mock_ov_client.connection_templates.get.return_value = {"uri": "uri"} + + self.mock_ansible_module.params = yaml.safe_load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=DICT_PARAMS_WITH_CHANGES) + ) + + def test_update_when_data_has_modified_attributes_but_bandwidth_is_equal(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['purpose'] = 'Management' + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + self.mock_ov_client.connection_templates.get.return_value = { + "bandwidth": DICT_PARAMS_WITH_CHANGES['bandwidth']} + + self.mock_ansible_module.params = yaml.safe_load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=data_merged) + ) + + def test_update_successfully_even_when_connection_template_uri_not_exists(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + del data_merged['connectionTemplateUri'] + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = yaml.safe_load(YAML_PARAMS_WITH_CHANGES) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_UPDATED, + ansible_facts=dict(ethernet_network=data_merged) + ) + + def test_rename_when_resource_exists(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['name'] = RENAMED_ETHERNET + params_to_rename = PARAMS_TO_RENAME.copy() + + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = params_to_rename + + EthernetNetworkModule().run() + + self.resource.update.assert_called_once_with(data_merged) + + def test_create_with_new_name_when_resource_not_exists(self): + data_merged = DEFAULT_ENET_TEMPLATE.copy() + data_merged['name'] = RENAMED_ETHERNET + params_to_rename = PARAMS_TO_RENAME.copy() + + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_ENET_TEMPLATE + + self.mock_ansible_module.params = params_to_rename + + EthernetNetworkModule().run() + + self.resource.create.assert_called_once_with(PARAMS_TO_RENAME['data']) + + def test_should_remove_ethernet_network(self): + self.resource.get_by.return_value = [DEFAULT_ENET_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_DELETED + ) + + def test_should_do_nothing_when_ethernet_network_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=EthernetNetworkModule.MSG_ALREADY_ABSENT + ) + + def test_should_create_all_ethernet_networks(self): + self.resource.get_range.side_effect = [[], DEFAULT_BULK_ENET_TEMPLATE] + self.resource.create_bulk.return_value = DEFAULT_BULK_ENET_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.resource.create_bulk.assert_called_once_with( + dict(namePrefix="TestNetwork", vlanIdRange="1-2,5,9-10")) + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_BULK_CREATED, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_should_create_missing_ethernet_networks(self): + enet_get_range_return = [ + {'name': 'TestNetwork_1', 'vlanId': 1}, + {'name': 'TestNetwork_2', 'vlanId': 2}, + ] + + self.resource.get_range.side_effect = [enet_get_range_return, DEFAULT_BULK_ENET_TEMPLATE] + self.resource.dissociate_values_or_ranges.return_value = [1, 2, 5, 9, 10] + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.resource.create_bulk.assert_called_once_with( + dict(namePrefix="TestNetwork", vlanIdRange="5,9,10")) + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, msg=EthernetNetworkModule.MSG_MISSING_BULK_CREATED, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_should_create_missing_ethernet_networks_with_just_one_difference(self): + enet_get_range_return = [ + {'name': 'TestNetwork_1', 'vlanId': 1}, + {'name': 'TestNetwork_2', 'vlanId': 2}, + ] + + self.resource.get_range.side_effect = [enet_get_range_return, DEFAULT_BULK_ENET_TEMPLATE] + self.resource.dissociate_values_or_ranges.return_value = [1, 2, 5] + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.resource.create_bulk.assert_called_once_with({'vlanIdRange': '5-5', 'namePrefix': 'TestNetwork'}) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=EthernetNetworkModule.MSG_MISSING_BULK_CREATED, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_should_do_nothing_when_ethernet_networks_already_exist(self): + self.resource.get_range.return_value = DEFAULT_BULK_ENET_TEMPLATE + self.resource.dissociate_values_or_ranges.return_value = [1, 2, 5, 9, 10] + + self.mock_ansible_module.params = PARAMS_FOR_BULK_CREATED + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, msg=EthernetNetworkModule.MSG_BULK_ALREADY_EXIST, + ansible_facts=dict(ethernet_network_bulk=DEFAULT_BULK_ENET_TEMPLATE)) + + def test_reset_successfully(self): + self.resource.get_by.return_value = [DICT_PARAMS_WITH_CHANGES] + self.mock_ov_client.connection_templates.update.return_value = {'result': 'success'} + self.mock_ov_client.connection_templates.get.return_value = { + "bandwidth": DICT_PARAMS_WITH_CHANGES['bandwidth']} + + self.mock_ov_client.connection_templates.get_default.return_value = {"bandwidth": { + "max": 1 + }} + + self.mock_ansible_module.params = yaml.safe_load(YAML_RESET_CONNECTION_TEMPLATE) + + EthernetNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, msg=EthernetNetworkModule.MSG_CONNECTION_TEMPLATE_RESET, + ansible_facts=dict(ethernet_network_connection_template={'result': 'success'})) + + def test_should_fail_when_reset_not_existing_ethernet_network(self): + self.resource.get_by.return_value = [None] + + self.mock_ansible_module.params = yaml.safe_load(YAML_RESET_CONNECTION_TEMPLATE) + + EthernetNetworkModule().run() + + self.mock_ansible_module.fail_json.assert_called_once_with( + exception=mock.ANY, + msg=EthernetNetworkModule.MSG_ETHERNET_NETWORK_NOT_FOUND + ) + + def test_update_scopes_when_different(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_ENET_TEMPLATE.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/ethernet/fake' + self.resource.get_by.return_value = [resource_data] + + patch_return = resource_data.copy() + patch_return['scopeUris'] = ['test'] + self.resource.patch.return_value = patch_return + + EthernetNetworkModule().run() + + self.resource.patch.assert_called_once_with('rest/ethernet/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(ethernet_network=patch_return), + msg=EthernetNetworkModule.MSG_UPDATED + ) + + def test_should_do_nothing_when_scopes_are_the_same(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_ENET_TEMPLATE.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + EthernetNetworkModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(ethernet_network=resource_data), + msg=EthernetNetworkModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network_info.py new file mode 100644 index 000000000..4a2813e2f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_ethernet_network_info.py @@ -0,0 +1,104 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import EthernetNetworkInfoModule +from .hpe_test_utils import FactsParamsTestCase + +ERROR_MSG = 'Fake message error' + +PARAMS_GET_ALL = dict( + config='config.json', + name=None +) + +PARAMS_GET_BY_NAME = dict( + config='config.json', + name="Test Ethernet Network", + options=[] +) + +PARAMS_GET_BY_NAME_WITH_OPTIONS = dict( + config='config.json', + name="Test Ethernet Network", + options=['associatedProfiles', 'associatedUplinkGroups'] +) + +PRESENT_ENETS = [{ + "name": "Test Ethernet Network", + "uri": "/rest/ethernet-networks/d34dcf5e-0d8e-441c-b00d-e1dd6a067188" +}] + +ENET_ASSOCIATED_UPLINK_GROUP_URIS = [ + "/rest/uplink-sets/c6bf9af9-48e7-4236-b08a-77684dc258a5", + "/rest/uplink-sets/e2f0031b-52bd-4223-9ac1-d91cb519d548" +] + +ENET_ASSOCIATED_PROFILE_URIS = [ + "/rest/server-profiles/83e2e117-59dc-4e33-9f24-462af951cbbe", + "/rest/server-profiles/57d3af2a-b6d2-4446-8645-f38dd808ea4d" +] + +ENET_ASSOCIATED_UPLINK_GROUPS = [dict(uri=ENET_ASSOCIATED_UPLINK_GROUP_URIS[0], name='Uplink Set 1'), + dict(uri=ENET_ASSOCIATED_UPLINK_GROUP_URIS[1], name='Uplink Set 2')] + +ENET_ASSOCIATED_PROFILES = [dict(uri=ENET_ASSOCIATED_PROFILE_URIS[0], name='Server Profile 1'), + dict(uri=ENET_ASSOCIATED_PROFILE_URIS[1], name='Server Profile 2')] + + +class EthernetNetworkInfoSpec(unittest.TestCase, + FactsParamsTestCase + ): + def setUp(self): + self.configure_mocks(self, EthernetNetworkInfoModule) + self.ethernet_networks = self.mock_ov_client.ethernet_networks + FactsParamsTestCase.configure_client_mock(self, self.ethernet_networks) + + def test_should_get_all_enets(self): + self.ethernet_networks.get_all.return_value = PRESENT_ENETS + self.mock_ansible_module.params = PARAMS_GET_ALL + + EthernetNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ethernet_networks=(PRESENT_ENETS) + ) + + def test_should_get_enet_by_name(self): + self.ethernet_networks.get_by.return_value = PRESENT_ENETS + self.mock_ansible_module.params = PARAMS_GET_BY_NAME + + EthernetNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ethernet_networks=(PRESENT_ENETS) + ) + + def test_should_get_enet_by_name_with_options(self): + self.ethernet_networks.get_by.return_value = PRESENT_ENETS + self.ethernet_networks.get_associated_profiles.return_value = ENET_ASSOCIATED_PROFILE_URIS + self.ethernet_networks.get_associated_uplink_groups.return_value = ENET_ASSOCIATED_UPLINK_GROUP_URIS + self.mock_ov_client.server_profiles.get.side_effect = ENET_ASSOCIATED_PROFILES + self.mock_ov_client.uplink_sets.get.side_effect = ENET_ASSOCIATED_UPLINK_GROUPS + + self.mock_ansible_module.params = PARAMS_GET_BY_NAME_WITH_OPTIONS + + EthernetNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ethernet_networks=PRESENT_ENETS, + enet_associated_profiles=ENET_ASSOCIATED_PROFILES, + enet_associated_uplink_groups=ENET_ASSOCIATED_UPLINK_GROUPS + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network.py new file mode 100644 index 000000000..6def80fc4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +# +# Copyright (2016-2017) Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import FcNetworkModule +from .hpe_test_utils import OneViewBaseTestCase + +FAKE_MSG_ERROR = 'Fake message error' + +DEFAULT_FC_NETWORK_TEMPLATE = dict( + name='New FC Network 2', + autoLoginRedistribution=True, + fabricType='FabricAttach' +) + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_FC_NETWORK_TEMPLATE['name']) +) + +PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_FC_NETWORK_TEMPLATE['name'], + newName="New Name", + fabricType='DirectAttach') +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_FC_NETWORK_TEMPLATE['name']) +) + + +class FcNetworkModuleSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase provides the mocks used in this test case + """ + + def setUp(self): + self.configure_mocks(self, FcNetworkModule) + self.resource = self.mock_ov_client.fc_networks + + def test_should_create_new_fc_network(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_FC_NETWORK_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + FcNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcNetworkModule.MSG_CREATED, + ansible_facts=dict(fc_network=DEFAULT_FC_NETWORK_TEMPLATE) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [DEFAULT_FC_NETWORK_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + FcNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=FcNetworkModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(fc_network=DEFAULT_FC_NETWORK_TEMPLATE) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = DEFAULT_FC_NETWORK_TEMPLATE.copy() + + data_merged['fabricType'] = 'DirectAttach' + + self.resource.get_by.return_value = [DEFAULT_FC_NETWORK_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + FcNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcNetworkModule.MSG_UPDATED, + ansible_facts=dict(fc_network=data_merged) + ) + + def test_should_remove_fc_network(self): + self.resource.get_by.return_value = [DEFAULT_FC_NETWORK_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + FcNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcNetworkModule.MSG_DELETED + ) + + def test_should_do_nothing_when_fc_network_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + FcNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=FcNetworkModule.MSG_ALREADY_ABSENT + ) + + def test_update_scopes_when_different(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_FC_NETWORK_TEMPLATE.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/fc/fake' + self.resource.get_by.return_value = [resource_data] + + patch_return = resource_data.copy() + patch_return['scopeUris'] = ['test'] + self.resource.patch.return_value = patch_return + + FcNetworkModule().run() + + self.resource.patch.assert_called_once_with('rest/fc/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(fc_network=patch_return), + msg=FcNetworkModule.MSG_UPDATED + ) + + def test_should_do_nothing_when_scopes_are_the_same(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_FC_NETWORK_TEMPLATE.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + FcNetworkModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(fc_network=resource_data), + msg=FcNetworkModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network_info.py new file mode 100644 index 000000000..236ce136a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fc_network_info.py @@ -0,0 +1,61 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import FcNetworkInfoModule +from .hpe_test_utils import FactsParamsTestCase + +ERROR_MSG = 'Fake message error' + +PARAMS_GET_ALL = dict( + config='config.json', + name=None +) + +PARAMS_GET_BY_NAME = dict( + config='config.json', + name="Test FC Network" +) + +PRESENT_NETWORKS = [{ + "name": "Test FC Network", + "uri": "/rest/fc-networks/c6bf9af9-48e7-4236-b08a-77684dc258a5" +}] + + +class FcNetworkInfoSpec(unittest.TestCase, + FactsParamsTestCase): + def setUp(self): + self.configure_mocks(self, FcNetworkInfoModule) + self.fc_networks = self.mock_ov_client.fc_networks + FactsParamsTestCase.configure_client_mock(self, self.fc_networks) + + def test_should_get_all_fc_networks(self): + self.fc_networks.get_all.return_value = PRESENT_NETWORKS + self.mock_ansible_module.params = PARAMS_GET_ALL + + FcNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + fc_networks=PRESENT_NETWORKS + ) + + def test_should_get_fc_network_by_name(self): + self.fc_networks.get_by.return_value = PRESENT_NETWORKS + self.mock_ansible_module.params = PARAMS_GET_BY_NAME + + FcNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + fc_networks=PRESENT_NETWORKS + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network.py new file mode 100644 index 000000000..224e5471e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +# +# Copyright (2016-2017) Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import FcoeNetworkModule +from .hpe_test_utils import OneViewBaseTestCase + +FAKE_MSG_ERROR = 'Fake message error' + +DEFAULT_FCOE_NETWORK_TEMPLATE = dict( + name='New FCoE Network 2', + vlanId="201", + connectionTemplateUri=None +) + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_FCOE_NETWORK_TEMPLATE['name']) +) + +PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_FCOE_NETWORK_TEMPLATE['name'], + fabricType='DirectAttach', + newName='New Name') +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_FCOE_NETWORK_TEMPLATE['name']) +) + + +class FcoeNetworkSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase provides the mocks used in this test case + """ + + def setUp(self): + self.configure_mocks(self, FcoeNetworkModule) + self.resource = self.mock_ov_client.fcoe_networks + + def test_should_create_new_fcoe_network(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_FCOE_NETWORK_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcoeNetworkModule.MSG_CREATED, + ansible_facts=dict(fcoe_network=DEFAULT_FCOE_NETWORK_TEMPLATE) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [DEFAULT_FCOE_NETWORK_TEMPLATE] + self.mock_ansible_module.params = PARAMS_FOR_PRESENT.copy() + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=FcoeNetworkModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(fcoe_network=DEFAULT_FCOE_NETWORK_TEMPLATE) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = DEFAULT_FCOE_NETWORK_TEMPLATE.copy() + data_merged['fabricType'] = 'DirectAttach' + + self.resource.get_by.return_value = [DEFAULT_FCOE_NETWORK_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcoeNetworkModule.MSG_UPDATED, + ansible_facts=dict(fcoe_network=data_merged) + ) + + def test_should_remove_fcoe_network(self): + self.resource.get_by.return_value = [DEFAULT_FCOE_NETWORK_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=FcoeNetworkModule.MSG_DELETED + ) + + def test_should_do_nothing_when_fcoe_network_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + FcoeNetworkModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=FcoeNetworkModule.MSG_ALREADY_ABSENT + ) + + def test_update_scopes_when_different(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_FCOE_NETWORK_TEMPLATE.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/fcoe/fake' + self.resource.get_by.return_value = [resource_data] + + patch_return = resource_data.copy() + patch_return['scopeUris'] = ['test'] + self.resource.patch.return_value = patch_return + + FcoeNetworkModule().run() + + self.resource.patch.assert_called_once_with('rest/fcoe/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(fcoe_network=patch_return), + msg=FcoeNetworkModule.MSG_UPDATED + ) + + def test_should_do_nothing_when_scopes_are_the_same(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_FCOE_NETWORK_TEMPLATE.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + FcoeNetworkModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(fcoe_network=resource_data), + msg=FcoeNetworkModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network_info.py new file mode 100644 index 000000000..387c1da3c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_fcoe_network_info.py @@ -0,0 +1,63 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import FcoeNetworkInfoModule +from .hpe_test_utils import FactsParamsTestCase + +ERROR_MSG = 'Fake message error' + +PARAMS_GET_ALL = dict( + config='config.json', + name=None +) + +PARAMS_GET_BY_NAME = dict( + config='config.json', + name="Test FCoE Networks" +) + +PRESENT_NETWORKS = [{ + "name": "Test FCoE Networks", + "uri": "/rest/fcoe-networks/c6bf9af9-48e7-4236-b08a-77684dc258a5" +}] + + +class FcoeNetworkInfoSpec(unittest.TestCase, + FactsParamsTestCase + ): + def setUp(self): + self.configure_mocks(self, FcoeNetworkInfoModule) + self.fcoe_networks = self.mock_ov_client.fcoe_networks + FactsParamsTestCase.configure_client_mock(self, self.fcoe_networks) + + def test_should_get_all_fcoe_network(self): + self.fcoe_networks.get_all.return_value = PRESENT_NETWORKS + self.mock_ansible_module.params = PARAMS_GET_ALL + + FcoeNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + fcoe_networks=PRESENT_NETWORKS + ) + + def test_should_get_fcoe_network_by_name(self): + self.fcoe_networks.get_by.return_value = PRESENT_NETWORKS + self.mock_ansible_module.params = PARAMS_GET_BY_NAME + + FcoeNetworkInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + fcoe_networks=PRESENT_NETWORKS + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group.py new file mode 100644 index 000000000..1f941fb50 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group.py @@ -0,0 +1,261 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 copy import deepcopy + +from ansible_collections.community.general.tests.unit.compat import unittest, mock +from .hpe_test_utils import OneViewBaseTestCase +from ansible_collections.community.general.plugins.modules.oneview_logical_interconnect_group import LogicalInterconnectGroupModule + + +FAKE_MSG_ERROR = 'Fake message error' + +DEFAULT_LIG_NAME = 'Test Logical Interconnect Group' +RENAMED_LIG = 'Renamed Logical Interconnect Group' + +DEFAULT_LIG_TEMPLATE = dict( + name=DEFAULT_LIG_NAME, + uplinkSets=[], + enclosureType='C7000', + interconnectMapTemplate=dict( + interconnectMapEntryTemplates=[] + ) +) + +PARAMS_LIG_TEMPLATE_WITH_MAP = dict( + config='config.json', + state='present', + data=dict( + name=DEFAULT_LIG_NAME, + uplinkSets=[], + enclosureType='C7000', + interconnectMapTemplate=dict( + interconnectMapEntryTemplates=[ + { + "logicalDownlinkUri": None, + "logicalLocation": { + "locationEntries": [ + { + "relativeValue": "1", + "type": "Bay" + }, + { + "relativeValue": 1, + "type": "Enclosure" + } + ] + }, + "permittedInterconnectTypeName": "HP VC Flex-10/10D Module" + }] + ) + )) + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_LIG_NAME) +) + +PARAMS_TO_RENAME = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_LIG_NAME, + newName=RENAMED_LIG) +) + +PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_LIG_NAME, + description='It is an example') +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_LIG_NAME) +) + + +class LogicalInterconnectGroupGeneralSpec(unittest.TestCase, + OneViewBaseTestCase): + def setUp(self): + self.configure_mocks(self, LogicalInterconnectGroupModule) + self.resource = self.mock_ov_client.logical_interconnect_groups + + def test_should_create_new_lig(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_LIG_TEMPLATE + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=LogicalInterconnectGroupModule.MSG_CREATED, + ansible_facts=dict(logical_interconnect_group=DEFAULT_LIG_TEMPLATE) + ) + + def test_should_create_new_with_named_permitted_interconnect_type(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = PARAMS_FOR_PRESENT + + self.mock_ansible_module.params = deepcopy(PARAMS_LIG_TEMPLATE_WITH_MAP) + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=LogicalInterconnectGroupModule.MSG_CREATED, + ansible_facts=dict(logical_interconnect_group=PARAMS_FOR_PRESENT.copy()) + ) + + def test_should_fail_when_permitted_interconnect_type_name_not_exists(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = PARAMS_FOR_PRESENT + self.mock_ov_client.interconnect_types.get_by.return_value = [] + + self.mock_ansible_module.params = deepcopy(PARAMS_LIG_TEMPLATE_WITH_MAP) + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.fail_json.assert_called_once_with( + exception=mock.ANY, + msg=LogicalInterconnectGroupModule.MSG_INTERCONNECT_TYPE_NOT_FOUND) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [DEFAULT_LIG_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=LogicalInterconnectGroupModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(logical_interconnect_group=DEFAULT_LIG_TEMPLATE) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = DEFAULT_LIG_TEMPLATE.copy() + data_merged['description'] = 'New description' + + self.resource.get_by.return_value = [DEFAULT_LIG_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=LogicalInterconnectGroupModule.MSG_UPDATED, + ansible_facts=dict(logical_interconnect_group=data_merged) + ) + + def test_rename_when_resource_exists(self): + data_merged = DEFAULT_LIG_TEMPLATE.copy() + data_merged['name'] = RENAMED_LIG + params_to_rename = PARAMS_TO_RENAME.copy() + + self.resource.get_by.return_value = [DEFAULT_LIG_TEMPLATE] + self.resource.update.return_value = data_merged + + self.mock_ansible_module.params = params_to_rename + + LogicalInterconnectGroupModule().run() + + self.resource.update.assert_called_once_with(data_merged) + + def test_create_with_newName_when_resource_not_exists(self): + data_merged = DEFAULT_LIG_TEMPLATE.copy() + data_merged['name'] = RENAMED_LIG + params_to_rename = PARAMS_TO_RENAME.copy() + + self.resource.get_by.return_value = [] + self.resource.create.return_value = DEFAULT_LIG_TEMPLATE + + self.mock_ansible_module.params = params_to_rename + + LogicalInterconnectGroupModule().run() + + self.resource.create.assert_called_once_with(PARAMS_TO_RENAME['data']) + + def test_should_remove_lig(self): + self.resource.get_by.return_value = [DEFAULT_LIG_TEMPLATE] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=LogicalInterconnectGroupModule.MSG_DELETED + ) + + def test_should_do_nothing_when_lig_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + LogicalInterconnectGroupModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=LogicalInterconnectGroupModule.MSG_ALREADY_ABSENT + ) + + def test_update_scopes_when_different(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_LIG_TEMPLATE.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/lig/fake' + self.resource.get_by.return_value = [resource_data] + + patch_return = resource_data.copy() + patch_return['scopeUris'] = ['test'] + self.resource.patch.return_value = patch_return + + LogicalInterconnectGroupModule().run() + + self.resource.patch.assert_called_once_with('rest/lig/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(logical_interconnect_group=patch_return), + msg=LogicalInterconnectGroupModule.MSG_UPDATED + ) + + def test_should_do_nothing_when_scopes_are_the_same(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = DEFAULT_LIG_TEMPLATE.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + LogicalInterconnectGroupModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(logical_interconnect_group=resource_data), + msg=LogicalInterconnectGroupModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group_info.py new file mode 100644 index 000000000..9fa602a8c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_logical_interconnect_group_info.py @@ -0,0 +1,63 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .hpe_test_utils import FactsParamsTestCase +from ansible_collections.community.general.plugins.modules.oneview_logical_interconnect_group_info import ( + LogicalInterconnectGroupInfoModule +) + + +ERROR_MSG = 'Fake message error' + +PARAMS_GET_ALL = dict( + config='config.json', + name=None +) + +PARAMS_GET_BY_NAME = dict( + config='config.json', + name="Test Logical Interconnect Group" +) + +PRESENT_LIGS = [{ + "name": "Test Logical Interconnect Group", + "uri": "/rest/logical-interconnect-groups/ebb4ada8-08df-400e-8fac-9ff987ac5140" +}] + + +class LogicalInterconnectGroupInfoSpec(unittest.TestCase, FactsParamsTestCase): + def setUp(self): + self.configure_mocks(self, LogicalInterconnectGroupInfoModule) + self.logical_interconnect_groups = self.mock_ov_client.logical_interconnect_groups + FactsParamsTestCase.configure_client_mock(self, self.logical_interconnect_groups) + + def test_should_get_all_ligs(self): + self.logical_interconnect_groups.get_all.return_value = PRESENT_LIGS + self.mock_ansible_module.params = PARAMS_GET_ALL + + LogicalInterconnectGroupInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + logical_interconnect_groups=(PRESENT_LIGS) + ) + + def test_should_get_lig_by_name(self): + self.logical_interconnect_groups.get_by.return_value = PRESENT_LIGS + self.mock_ansible_module.params = PARAMS_GET_BY_NAME + + LogicalInterconnectGroupInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + logical_interconnect_groups=(PRESENT_LIGS) + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set.py new file mode 100644 index 000000000..f801cd102 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set.py @@ -0,0 +1,187 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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, mock +from .hpe_test_utils import OneViewBaseTestCase +from .oneview_module_loader import NetworkSetModule + +FAKE_MSG_ERROR = 'Fake message error' + +NETWORK_SET = dict( + name='OneViewSDK Test Network Set', + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc'] +) + +NETWORK_SET_WITH_NEW_NAME = dict(name='OneViewSDK Test Network Set - Renamed') + +PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=dict(name=NETWORK_SET['name'], + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc']) +) + +PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=NETWORK_SET['name'], + newName=NETWORK_SET['name'] + " - Renamed", + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc', 'Name of a Network']) +) + +PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=NETWORK_SET['name']) +) + + +class NetworkSetModuleSpec(unittest.TestCase, + OneViewBaseTestCase): + """ + OneViewBaseTestCase has common tests for class constructor and main function, + also provides the mocks used in this test case. + """ + + def setUp(self): + self.configure_mocks(self, NetworkSetModule) + self.resource = self.mock_ov_client.network_sets + self.ethernet_network_client = self.mock_ov_client.ethernet_networks + + def test_should_create_new_network_set(self): + self.resource.get_by.return_value = [] + self.resource.create.return_value = NETWORK_SET + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=NetworkSetModule.MSG_CREATED, + ansible_facts=dict(network_set=NETWORK_SET) + ) + + def test_should_not_update_when_data_is_equals(self): + self.resource.get_by.return_value = [NETWORK_SET] + + self.mock_ansible_module.params = PARAMS_FOR_PRESENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=NetworkSetModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(network_set=NETWORK_SET) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = dict(name=NETWORK_SET['name'] + " - Renamed", + networkUris=['/rest/ethernet-networks/aaa-bbb-ccc', + '/rest/ethernet-networks/ddd-eee-fff'] + ) + + self.resource.get_by.side_effect = [NETWORK_SET], [] + self.resource.update.return_value = data_merged + self.ethernet_network_client.get_by.return_value = [{'uri': '/rest/ethernet-networks/ddd-eee-fff'}] + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=NetworkSetModule.MSG_UPDATED, + ansible_facts=dict(network_set=data_merged) + ) + + def test_should_raise_exception_when_ethernet_network_not_found(self): + self.resource.get_by.side_effect = [NETWORK_SET], [] + self.ethernet_network_client.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_WITH_CHANGES + + NetworkSetModule().run() + + self.mock_ansible_module.fail_json.assert_called_once_with( + exception=mock.ANY, + msg=NetworkSetModule.MSG_ETHERNET_NETWORK_NOT_FOUND + "Name of a Network" + ) + + def test_should_remove_network(self): + self.resource.get_by.return_value = [NETWORK_SET] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=NetworkSetModule.MSG_DELETED + ) + + def test_should_do_nothing_when_network_set_not_exist(self): + self.resource.get_by.return_value = [] + + self.mock_ansible_module.params = PARAMS_FOR_ABSENT + + NetworkSetModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=NetworkSetModule.MSG_ALREADY_ABSENT + ) + + def test_update_scopes_when_different(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = NETWORK_SET.copy() + resource_data['scopeUris'] = ['fake'] + resource_data['uri'] = 'rest/network-sets/fake' + self.resource.get_by.return_value = [resource_data] + + patch_return = resource_data.copy() + patch_return['scopeUris'] = ['test'] + self.resource.patch.return_value = patch_return + + NetworkSetModule().run() + + self.resource.patch.assert_called_once_with('rest/network-sets/fake', + operation='replace', + path='/scopeUris', + value=['test']) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + ansible_facts=dict(network_set=patch_return), + msg=NetworkSetModule.MSG_UPDATED + ) + + def test_should_do_nothing_when_scopes_are_the_same(self): + params_to_scope = PARAMS_FOR_PRESENT.copy() + params_to_scope['data']['scopeUris'] = ['test'] + self.mock_ansible_module.params = params_to_scope + + resource_data = NETWORK_SET.copy() + resource_data['scopeUris'] = ['test'] + self.resource.get_by.return_value = [resource_data] + + NetworkSetModule().run() + + self.resource.patch.not_been_called() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + ansible_facts=dict(network_set=resource_data), + msg=NetworkSetModule.MSG_ALREADY_PRESENT + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set_info.py new file mode 100644 index 000000000..13cd0400a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_network_set_info.py @@ -0,0 +1,121 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import NetworkSetInfoModule +from .hpe_test_utils import FactsParamsTestCase + +ERROR_MSG = 'Fake message error' + +PARAMS_GET_ALL = dict( + config='config.json', + name=None +) + +PARAMS_GET_ALL_WITHOUT_ETHERNET = dict( + config='config.json', + name=None, + options=['withoutEthernet'] +) + +PARAMS_GET_BY_NAME = dict( + config='config.json', + name='Network Set 1' +) + +PARAMS_GET_BY_NAME_WITHOUT_ETHERNET = dict( + config='config.json', + name='Network Set 1', + options=['withoutEthernet'] +) + + +class NetworkSetInfoSpec(unittest.TestCase, + FactsParamsTestCase): + def setUp(self): + self.configure_mocks(self, NetworkSetInfoModule) + self.network_sets = self.mock_ov_client.network_sets + FactsParamsTestCase.configure_client_mock(self, self.network_sets) + + def test_should_get_all_network_sets(self): + network_sets = [{ + "name": "Network Set 1", + "networkUris": ['/rest/ethernet-networks/aaa-bbb-ccc'] + }, { + "name": "Network Set 2", + "networkUris": ['/rest/ethernet-networks/ddd-eee-fff', '/rest/ethernet-networks/ggg-hhh-fff'] + }] + + self.network_sets.get_all.return_value = network_sets + self.mock_ansible_module.params = PARAMS_GET_ALL + + NetworkSetInfoModule().run() + + self.network_sets.get_all.assert_called_once_with() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + network_sets=network_sets) + + def test_should_get_all_network_sets_without_ethernet(self): + network_sets = [{ + "name": "Network Set 1", + "networkUris": [] + }, { + "name": "Network Set 2", + "networkUris": [] + }] + + self.network_sets.get_all.return_value = network_sets + self.mock_ansible_module.params = PARAMS_GET_ALL + + NetworkSetInfoModule().run() + + self.network_sets.get_all.assert_called_once_with() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + network_sets=network_sets) + + def test_should_get_network_set_by_name(self): + network_sets = [{ + "name": "Network Set 1", + "networkUris": ['/rest/ethernet-networks/aaa-bbb-ccc'] + }] + + self.network_sets.get_by.return_value = network_sets + self.mock_ansible_module.params = PARAMS_GET_BY_NAME + + NetworkSetInfoModule().run() + + self.network_sets.get_by.assert_called_once_with('name', 'Network Set 1') + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + network_sets=network_sets) + + def test_should_get_network_set_by_name_without_ethernet(self): + network_sets = [{ + "name": "Network Set 1", + "networkUris": [] + }] + + self.network_sets.get_all_without_ethernet.return_value = network_sets + self.mock_ansible_module.params = PARAMS_GET_BY_NAME_WITHOUT_ETHERNET + + NetworkSetInfoModule().run() + + expected_filter = "\"'name'='Network Set 1'\"" + self.network_sets.get_all_without_ethernet.assert_called_once_with(filter=expected_filter) + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + network_sets=network_sets) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager.py new file mode 100644 index 000000000..d675c3b35 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager.py @@ -0,0 +1,243 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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, mock +from .oneview_module_loader import SanManagerModule +from .hpe_test_utils import OneViewBaseTestCase +from copy import deepcopy + +FAKE_MSG_ERROR = 'Fake message error' + +DEFAULT_SAN_MANAGER_TEMPLATE = dict( + name='172.18.15.1', + providerDisplayName='Brocade Network Advisor', + uri='/rest/fc-sans/device-managers/UUU-AAA-BBB', + refreshState='OK', + connectionInfo=[ + { + 'valueFormat': 'IPAddressOrHostname', + 'displayName': 'Host', + 'name': 'Host', + 'valueType': 'String', + 'required': False, + 'value': '172.18.15.1' + }] +) + + +class SanManagerModuleSpec(unittest.TestCase, + OneViewBaseTestCase): + PARAMS_FOR_PRESENT = dict( + config='config.json', + state='present', + data=DEFAULT_SAN_MANAGER_TEMPLATE + ) + + PARAMS_FOR_CONNECTION_INFORMATION_SET = dict( + config='config.json', + state='connection_information_set', + data=DEFAULT_SAN_MANAGER_TEMPLATE.copy() + ) + + PARAMS_WITH_CHANGES = dict( + config='config.json', + state='present', + data=dict(name=DEFAULT_SAN_MANAGER_TEMPLATE['name'], + refreshState='RefreshPending') + ) + + PARAMS_FOR_ABSENT = dict( + config='config.json', + state='absent', + data=dict(name=DEFAULT_SAN_MANAGER_TEMPLATE['name']) + ) + + def setUp(self): + self.configure_mocks(self, SanManagerModule) + self.resource = self.mock_ov_client.san_managers + + def test_should_add_new_san_manager(self): + self.resource.get_by_name.return_value = [] + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + self.resource.add.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + + self.mock_ansible_module.params = self.PARAMS_FOR_PRESENT + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=SanManagerModule.MSG_CREATED, + ansible_facts=dict(san_manager=DEFAULT_SAN_MANAGER_TEMPLATE) + ) + + def test_should_find_provider_uri_to_add(self): + self.resource.get_by_name.return_value = [] + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + self.resource.add.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + + self.mock_ansible_module.params = self.PARAMS_FOR_PRESENT + + SanManagerModule().run() + + provider_display_name = DEFAULT_SAN_MANAGER_TEMPLATE['providerDisplayName'] + self.resource.get_provider_uri.assert_called_once_with(provider_display_name) + + def test_should_not_update_when_data_is_equals(self): + output_data = deepcopy(DEFAULT_SAN_MANAGER_TEMPLATE) + output_data.pop('connectionInfo') + self.resource.get_by_name.return_value = deepcopy(DEFAULT_SAN_MANAGER_TEMPLATE) + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + + self.mock_ansible_module.params = self.PARAMS_FOR_PRESENT + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=SanManagerModule.MSG_ALREADY_PRESENT, + ansible_facts=dict(san_manager=output_data) + ) + + def test_update_when_data_has_modified_attributes(self): + data_merged = deepcopy(DEFAULT_SAN_MANAGER_TEMPLATE) + data_merged['fabricType'] = 'DirectAttach' + + self.resource.get_by_name.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + + self.resource.update.return_value = data_merged + self.mock_ansible_module.params = self.PARAMS_WITH_CHANGES + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=SanManagerModule.MSG_UPDATED, + ansible_facts=dict(san_manager=data_merged) + ) + + def test_update_should_not_send_connection_info_when_not_informed_on_data(self): + merged_data = deepcopy(DEFAULT_SAN_MANAGER_TEMPLATE) + merged_data['refreshState'] = 'RefreshPending' + output_data = deepcopy(merged_data) + output_data.pop('connectionInfo') + + self.resource.get_by_name.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + + self.resource.update.return_value = merged_data + self.mock_ansible_module.params = self.PARAMS_WITH_CHANGES + + SanManagerModule().run() + + self.resource.update.assert_called_once_with(resource=output_data, id_or_uri=output_data['uri']) + + def test_should_remove_san_manager(self): + self.resource.get_by_name.return_value = deepcopy(DEFAULT_SAN_MANAGER_TEMPLATE) + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + + self.mock_ansible_module.params = self.PARAMS_FOR_ABSENT.copy() + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=SanManagerModule.MSG_DELETED + ) + + def test_should_do_nothing_when_san_manager_not_exist(self): + self.resource.get_by_name.return_value = [] + + self.mock_ansible_module.params = self.PARAMS_FOR_ABSENT.copy() + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + msg=SanManagerModule.MSG_ALREADY_ABSENT + ) + + def test_should_fail_when_name_not_found(self): + self.resource.get_by_name.return_value = [] + self.resource.get_provider_uri.return_value = None + + self.mock_ansible_module.params = self.PARAMS_FOR_PRESENT + + SanManagerModule().run() + + self.mock_ansible_module.fail_json.assert_called_once_with( + exception=mock.ANY, + msg="The provider 'Brocade Network Advisor' was not found." + ) + + def test_should_fail_when_name_and_hosts_in_connectionInfo_missing(self): + bad_params = deepcopy(self.PARAMS_FOR_PRESENT) + bad_params['data'].pop('name') + bad_params['data'].pop('connectionInfo') + + self.mock_ansible_module.params = bad_params + + SanManagerModule().run() + + msg = 'A "name" or "connectionInfo" must be provided inside the "data" field for this operation. ' + msg += 'If a "connectionInfo" is provided, the "Host" name is considered as the "name" for the resource.' + + self.mock_ansible_module.fail_json.assert_called_once_with(exception=mock.ANY, msg=msg) + + def test_connection_information_set_should_set_the_connection_information(self): + data_merged = deepcopy(DEFAULT_SAN_MANAGER_TEMPLATE) + data_merged['fabricType'] = 'DirectAttach' + + self.resource.get_by_name.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + + self.resource.update.return_value = data_merged + self.mock_ansible_module.params = self.PARAMS_FOR_CONNECTION_INFORMATION_SET + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=SanManagerModule.MSG_UPDATED, + ansible_facts=dict(san_manager=data_merged) + ) + + def test_should_add_new_san_manager_when_connection_information_set_called_without_resource(self): + self.resource.get_by_name.return_value = [] + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + self.resource.add.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + + self.mock_ansible_module.params = self.PARAMS_FOR_CONNECTION_INFORMATION_SET + + SanManagerModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=True, + msg=SanManagerModule.MSG_CREATED, + ansible_facts=dict(san_manager=DEFAULT_SAN_MANAGER_TEMPLATE) + ) + + def test_should_fail_when_required_attribute_missing(self): + bad_params = deepcopy(self.PARAMS_FOR_CONNECTION_INFORMATION_SET) + bad_params['data'] = self.PARAMS_FOR_CONNECTION_INFORMATION_SET['data'].copy() + bad_params['data'].pop('connectionInfo') + + self.resource.get_by_name.return_value = DEFAULT_SAN_MANAGER_TEMPLATE + self.resource.get_provider_uri.return_value = '/rest/fc-sans/providers/123/device-managers' + + self.mock_ansible_module.params = bad_params + + SanManagerModule().run() + + msg = 'A connectionInfo field is required for this operation.' + + self.mock_ansible_module.fail_json.assert_called_once_with(exception=mock.ANY, msg=msg) + + +if __name__ == '__main__': + unittest.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager_info.py new file mode 100644 index 000000000..be1f24316 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_oneview_san_manager_info.py @@ -0,0 +1,72 @@ +# Copyright (c) 2016-2017 Hewlett Packard Enterprise Development LP +# 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 .oneview_module_loader import SanManagerInfoModule +from .hpe_test_utils import FactsParamsTestCase + + +class SanManagerInfoSpec(unittest.TestCase, FactsParamsTestCase): + ERROR_MSG = 'Fake message error' + + PARAMS_GET_ALL = dict( + config='config.json', + provider_display_name=None + ) + + PARAMS_GET_BY_PROVIDER_DISPLAY_NAME = dict( + config='config.json', + provider_display_name="Brocade Network Advisor" + ) + + PRESENT_SAN_MANAGERS = [{ + "providerDisplayName": "Brocade Network Advisor", + "uri": "/rest/fc-sans/device-managers//d60efc8a-15b8-470c-8470-738d16d6b319" + }] + + def setUp(self): + self.configure_mocks(self, SanManagerInfoModule) + self.san_managers = self.mock_ov_client.san_managers + + FactsParamsTestCase.configure_client_mock(self, self.san_managers) + + def test_should_get_all(self): + self.san_managers.get_all.return_value = self.PRESENT_SAN_MANAGERS + self.mock_ansible_module.params = self.PARAMS_GET_ALL + + SanManagerInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + san_managers=self.PRESENT_SAN_MANAGERS + ) + + def test_should_get_by_display_name(self): + self.san_managers.get_by_provider_display_name.return_value = self.PRESENT_SAN_MANAGERS[0] + self.mock_ansible_module.params = self.PARAMS_GET_BY_PROVIDER_DISPLAY_NAME + + SanManagerInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + san_managers=self.PRESENT_SAN_MANAGERS + ) + + def test_should_return_empty_list_when_get_by_display_name_is_null(self): + self.san_managers.get_by_provider_display_name.return_value = None + self.mock_ansible_module.params = self.PARAMS_GET_BY_PROVIDER_DISPLAY_NAME + + SanManagerInfoModule().run() + + self.mock_ansible_module.exit_json.assert_called_once_with( + changed=False, + san_managers=[] + ) + + +if __name__ == '__main__': + unittest.main() 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 new file mode 100644 index 000000000..8e52368ff --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py @@ -0,0 +1,241 @@ +# -*- 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 + +import json + +from collections import namedtuple +from ansible_collections.community.general.plugins.modules import opkg + +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 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pacman.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pacman.py new file mode 100644 index 000000000..04ff5bb3e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pacman.py @@ -0,0 +1,1099 @@ +# 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.module_utils import basic +from ansible_collections.community.general.tests.unit.compat import mock +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + set_module_args, + exit_json, + fail_json, +) + +from ansible_collections.community.general.plugins.modules import pacman +from ansible_collections.community.general.plugins.modules.pacman import ( + Package, + VersionTuple, +) + +import pytest + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + return arg + + +# This inventory data is tightly coupled with the inventory test and the mock_valid_inventory fixture +valid_inventory = { + "installed_pkgs": { + "file": "5.41-1", + "filesystem": "2021.11.11-1", + "findutils": "4.8.0-1", + "gawk": "5.1.1-1", + "gettext": "0.21-1", + "grep": "3.7-1", + "gzip": "1.11-1", + "pacman": "6.0.1-2", + "pacman-mirrorlist": "20211114-1", + "sed": "4.8-1", + "sqlite": "3.36.0-1", + }, + "installed_groups": { + "base-devel": set(["gawk", "grep", "file", "findutils", "pacman", "sed", "gzip", "gettext"]) + }, + "available_pkgs": { + "acl": "2.3.1-1", + "amd-ucode": "20211027.1d00989-1", + "archlinux-keyring": "20211028-1", + "argon2": "20190702-3", + "attr": "2.5.1-1", + "audit": "3.0.6-5", + "autoconf": "2.71-1", + "automake": "1.16.5-1", + "b43-fwcutter": "019-3", + "gawk": "5.1.1-1", + "grep": "3.7-1", + "sqlite": "3.37.0-1", + "sudo": "1.9.8.p2-3", + }, + "available_groups": { + "base-devel": set( + [ + "libtool", + "gawk", + "which", + "texinfo", + "fakeroot", + "grep", + "findutils", + "autoconf", + "gzip", + "pkgconf", + "flex", + "patch", + "groff", + "m4", + "bison", + "gcc", + "gettext", + "make", + "file", + "pacman", + "sed", + "automake", + "sudo", + "binutils", + ] + ), + "some-group": set(["libtool", "sudo", "binutils"]), + }, + "upgradable_pkgs": { + "sqlite": VersionTuple(current="3.36.0-1", latest="3.37.0-1"), + }, + "pkg_reasons": { + "file": "explicit", + "filesystem": "explicit", + "findutils": "explicit", + "gawk": "explicit", + "gettext": "explicit", + "grep": "explicit", + "gzip": "explicit", + "pacman": "explicit", + "pacman-mirrorlist": "dependency", + "sed": "explicit", + "sqlite": "explicit", + }, +} + +empty_inventory = { + "installed_pkgs": {}, + "available_pkgs": {}, + "installed_groups": {}, + "available_groups": {}, + "upgradable_pkgs": {}, + "pkg_reasons": {}, +} + + +class TestPacman: + @pytest.fixture(autouse=True) + def run_command(self, mocker): + self.mock_run_command = mocker.patch.object(basic.AnsibleModule, "run_command", autospec=True) + + @pytest.fixture + def mock_package_list(self, mocker): + return mocker.patch.object(pacman.Pacman, "package_list", autospec=True) + + @pytest.fixture(autouse=True) + def common(self, mocker): + self.mock_module = mocker.patch.multiple( + basic.AnsibleModule, + exit_json=exit_json, + fail_json=fail_json, + get_bin_path=get_bin_path, + ) + + @pytest.fixture + def mock_empty_inventory(self, mocker): + inv = empty_inventory + return mocker.patch.object(pacman.Pacman, "_build_inventory", return_value=inv) + + @pytest.fixture + def mock_valid_inventory(self, mocker): + return mocker.patch.object(pacman.Pacman, "_build_inventory", return_value=valid_inventory) + + def test_fail_without_required_args(self): + with pytest.raises(AnsibleFailJson) as e: + set_module_args({}) + pacman.main() + assert e.match(r"one of the following is required") + + def test_success(self, mock_empty_inventory): + set_module_args({"update_cache": True}) # Simplest args to let init go through + P = pacman.Pacman(pacman.setup_module()) + with pytest.raises(AnsibleExitJson) as e: + P.success() + + def test_fail(self, mock_empty_inventory): + set_module_args({"update_cache": True}) + P = pacman.Pacman(pacman.setup_module()) + + args = dict( + msg="msg", stdout="something", stderr="somethingelse", cmd=["command", "with", "args"], rc=1 + ) + with pytest.raises(AnsibleFailJson) as e: + P.fail(**args) + + assert all(item in e.value.args[0] for item in args) + + @pytest.mark.parametrize( + "expected, run_command_side_effect, raises", + [ + ( + # Regular run + valid_inventory, + [ + [ # pacman --query + 0, + """file 5.41-1 + filesystem 2021.11.11-1 + findutils 4.8.0-1 + gawk 5.1.1-1 + gettext 0.21-1 + grep 3.7-1 + gzip 1.11-1 + pacman 6.0.1-2 + pacman-mirrorlist 20211114-1 + sed 4.8-1 + sqlite 3.36.0-1 + """, + "", + ], + ( # pacman --query --group + 0, + """base-devel file + base-devel findutils + base-devel gawk + base-devel gettext + base-devel grep + base-devel gzip + base-devel pacman + base-devel sed + """, + "", + ), + ( # pacman --sync --list + 0, + """core acl 2.3.1-1 [installed] + core amd-ucode 20211027.1d00989-1 + core archlinux-keyring 20211028-1 [installed] + core argon2 20190702-3 [installed] + core attr 2.5.1-1 [installed] + core audit 3.0.6-5 [installed: 3.0.6-2] + core autoconf 2.71-1 + core automake 1.16.5-1 + core b43-fwcutter 019-3 + core gawk 5.1.1-1 [installed] + core grep 3.7-1 [installed] + core sqlite 3.37.0-1 [installed: 3.36.0-1] + code sudo 1.9.8.p2-3 + """, + "", + ), + ( # pacman --sync --group --group + 0, + """base-devel autoconf + base-devel automake + base-devel binutils + base-devel bison + base-devel fakeroot + base-devel file + base-devel findutils + base-devel flex + base-devel gawk + base-devel gcc + base-devel gettext + base-devel grep + base-devel groff + base-devel gzip + base-devel libtool + base-devel m4 + base-devel make + base-devel pacman + base-devel patch + base-devel pkgconf + base-devel sed + base-devel sudo + base-devel texinfo + base-devel which + some-group libtool + some-group sudo + some-group binutils + """, + "", + ), + ( # pacman --query --upgrades + 0, + """sqlite 3.36.0-1 -> 3.37.0-1 + systemd 249.6-3 -> 249.7-2 [ignored] + """, + "", + ), + ( # pacman --query --explicit + 0, + """file 5.41-1 + filesystem 2021.11.11-1 + findutils 4.8.0-1 + gawk 5.1.1-1 + gettext 0.21-1 + grep 3.7-1 + gzip 1.11-1 + pacman 6.0.1-2 + sed 4.8-1 + sqlite 3.36.0-1 + """, + "", + ), + ( # pacman --query --deps + 0, + """pacman-mirrorlist 20211114-1 + """, + "", + ), + ], + None, + ), + ( + # All good, but call to --query --upgrades return 1. aka nothing to upgrade + # with a pacman warning + empty_inventory, + [ + (0, "", ""), + (0, "", ""), + (0, "", ""), + (0, "", ""), + ( + 1, + "", + "warning: config file /etc/pacman.conf, line 34: directive 'TotalDownload' in section 'options' not recognized.", + ), + (0, "", ""), + (0, "", ""), + ], + None, + ), + ( + # failure + empty_inventory, + [ + (0, "", ""), + (0, "", ""), + (0, "", ""), + (0, "", ""), + ( + 1, + "partial\npkg\\nlist", + "some warning", + ), + (0, "", ""), + (0, "", ""), + ], + AnsibleFailJson, + ), + ], + ) + def test_build_inventory(self, expected, run_command_side_effect, raises): + self.mock_run_command.side_effect = run_command_side_effect + + set_module_args({"update_cache": True}) + if raises: + with pytest.raises(raises): + P = pacman.Pacman(pacman.setup_module()) + P._build_inventory() + else: + P = pacman.Pacman(pacman.setup_module()) + assert P._build_inventory() == expected + + @pytest.mark.parametrize("check_mode_value", [True, False]) + def test_upgrade_check_empty_inventory(self, mock_empty_inventory, check_mode_value): + set_module_args({"upgrade": True, "_ansible_check_mode": check_mode_value}) + P = pacman.Pacman(pacman.setup_module()) + with pytest.raises(AnsibleExitJson) as e: + P.run() + self.mock_run_command.call_count == 0 + out = e.value.args[0] + assert "packages" not in out + assert not out["changed"] + assert "diff" not in out + + def test_update_db_check(self, mock_empty_inventory): + set_module_args({"update_cache": True, "_ansible_check_mode": True}) + P = pacman.Pacman(pacman.setup_module()) + + with pytest.raises(AnsibleExitJson) as e: + P.run() + self.mock_run_command.call_count == 0 + out = e.value.args[0] + assert "packages" not in out + assert out["changed"] + + @pytest.mark.parametrize( + "module_args,expected_calls,changed", + [ + ( + {}, + [ + (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'a\nb\nc', ''), + (["pacman", "--sync", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'), + (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'b\na\nc', ''), + ], + False, + ), + ( + {"force": True}, + [ + (["pacman", "--sync", "--refresh", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'), + ], + True, + ), + ( + {"update_cache_extra_args": "--some-extra args"}, # shlex test + [ + (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'a\nb\nc', ''), + (["pacman", "--sync", "--refresh", "--some-extra", "args"], {'check_rc': False}, 0, 'stdout', 'stderr'), + (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'a changed\nb\nc', ''), + ], + True, + ), + ( + {"force": True, "update_cache_extra_args": "--some-extra args"}, + [ + (["pacman", "--sync", "--refresh", "--some-extra", "args", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'), + ], + True, + ), + ( + # Test whether pacman --sync --list is not called more than twice + {"upgrade": True}, + [ + (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'core foo 1.0.0-1 [installed]', ''), + (["pacman", "--sync", "--refresh"], {'check_rc': False}, 0, 'stdout', 'stderr'), + (["pacman", "--sync", "--list"], {'check_rc': True}, 0, 'core foo 1.0.0-1 [installed]', ''), + # The following is _build_inventory: + (["pacman", "--query"], {'check_rc': True}, 0, 'foo 1.0.0-1', ''), + (["pacman", "--query", "--groups"], {'check_rc': True}, 0, '', ''), + (["pacman", "--sync", "--groups", "--groups"], {'check_rc': True}, 0, '', ''), + (["pacman", "--query", "--upgrades"], {'check_rc': False}, 0, '', ''), + (["pacman", "--query", "--explicit"], {'check_rc': True}, 0, 'foo 1.0.0-1', ''), + (["pacman", "--query", "--deps"], {'check_rc': True}, 0, '', ''), + ], + False, + ), + ], + ) + def test_update_db(self, module_args, expected_calls, changed): + args = {"update_cache": True} + args.update(module_args) + set_module_args(args) + + self.mock_run_command.side_effect = [ + (rc, stdout, stderr) for expected_call, kwargs, rc, stdout, stderr in expected_calls + ] + with pytest.raises(AnsibleExitJson) as e: + P = pacman.Pacman(pacman.setup_module()) + P.run() + + self.mock_run_command.assert_has_calls([ + mock.call(mock.ANY, expected_call, **kwargs) for expected_call, kwargs, rc, stdout, stderr in expected_calls + ]) + out = e.value.args[0] + assert out["cache_updated"] == changed + assert out["changed"] == changed + + @pytest.mark.parametrize( + "check_mode_value, run_command_data, upgrade_extra_args", + [ + # just check + (True, None, None), + ( + # for real + False, + { + "args": ["pacman", "--sync", "--sysupgrade", "--quiet", "--noconfirm"], + "return_value": [0, "stdout", "stderr"], + }, + None, + ), + ( + # with extra args + False, + { + "args": [ + "pacman", + "--sync", + "--sysupgrade", + "--quiet", + "--noconfirm", + "--some", + "value", + ], + "return_value": [0, "stdout", "stderr"], + }, + "--some value", + ), + ], + ) + def test_upgrade(self, mock_valid_inventory, check_mode_value, run_command_data, upgrade_extra_args): + args = {"upgrade": True, "_ansible_check_mode": check_mode_value} + if upgrade_extra_args: + args["upgrade_extra_args"] = upgrade_extra_args + set_module_args(args) + + if run_command_data and "return_value" in run_command_data: + self.mock_run_command.return_value = run_command_data["return_value"] + + P = pacman.Pacman(pacman.setup_module()) + + with pytest.raises(AnsibleExitJson) as e: + P.run() + out = e.value.args[0] + + if check_mode_value: + self.mock_run_command.call_count == 0 + + if run_command_data and "args" in run_command_data: + self.mock_run_command.assert_called_with(mock.ANY, run_command_data["args"], check_rc=False) + assert out["stdout"] == "stdout" + assert out["stderr"] == "stderr" + + assert len(out["packages"]) == 1 and "sqlite" in out["packages"] + assert out["changed"] + assert out["diff"]["before"] and out["diff"]["after"] + + def test_upgrade_fail(self, mock_valid_inventory): + set_module_args({"upgrade": True}) + self.mock_run_command.return_value = [1, "stdout", "stderr"] + P = pacman.Pacman(pacman.setup_module()) + + with pytest.raises(AnsibleFailJson) as e: + P.run() + self.mock_run_command.call_count == 1 + out = e.value.args[0] + assert out["failed"] + assert out["stdout"] == "stdout" + assert out["stderr"] == "stderr" + + @pytest.mark.parametrize( + "state, pkg_names, expected, run_command_data, raises", + [ + # regular packages, no resolving required + ( + "present", + ["acl", "attr"], + [Package(name="acl", source="acl"), Package(name="attr", source="attr")], + None, + None, + ), + ( + # group expansion + "present", + ["acl", "some-group", "attr"], + [ + Package(name="acl", source="acl"), + Package(name="binutils", source="binutils"), + Package(name="libtool", source="libtool"), + Package(name="sudo", source="sudo"), + Package(name="attr", source="attr"), + ], + None, + None, + ), + ( + # / format -> call to pacman to resolve + "present", + ["community/elixir"], + [Package(name="elixir", source="community/elixir")], + { + "calls": [ + mock.call( + mock.ANY, + ["pacman", "--sync", "--print-format", "%n", "community/elixir"], + check_rc=False, + ) + ], + "side_effect": [(0, "elixir", "")], + }, + None, + ), + ( + # catch all -> call to pacman to resolve (--sync and --upgrade) + "present", + ["somepackage-12.3-x86_64.pkg.tar.zst"], + [ + Package( + name="somepackage", + source="somepackage-12.3-x86_64.pkg.tar.zst", + source_is_URL=True, + ) + ], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--sync", + "--print-format", + "%n", + "somepackage-12.3-x86_64.pkg.tar.zst", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--upgrade", + "--print-format", + "%n", + "somepackage-12.3-x86_64.pkg.tar.zst", + ], + check_rc=False, + ), + ], + "side_effect": [(1, "", "nope"), (0, "somepackage", "")], + }, + None, + ), + ( + # install a package that doesn't exist. call pacman twice and give up + "present", + ["unknown-package"], + [], + { + # no call validation, since it will fail + "side_effect": [(1, "", "nope"), (1, "", "stillnope")], + }, + AnsibleFailJson, + ), + ( + # Edge case: resolve a pkg that doesn't exist when trying to remove it (state == absent). + # will fallback to file + url format but not complain since it is already not there + # Can happen if a pkg is removed for the repos (or if a repo is disabled/removed) + "absent", + ["unknown-package-to-remove"], + [], + { + "calls": [ + mock.call( + mock.ANY, + ["pacman", "--sync", "--print-format", "%n", "unknown-package-to-remove"], + check_rc=False, + ), + mock.call( + mock.ANY, + ["pacman", "--upgrade", "--print-format", "%n", "unknown-package-to-remove"], + check_rc=False, + ), + ], + "side_effect": [(1, "", "nope"), (1, "", "stillnope")], + }, + None, # Doesn't fail + ), + ], + ) + def test_package_list( + self, mock_valid_inventory, state, pkg_names, expected, run_command_data, raises + ): + set_module_args({"name": pkg_names, "state": state}) + P = pacman.Pacman(pacman.setup_module()) + P.inventory = P._build_inventory() + if run_command_data: + self.mock_run_command.side_effect = run_command_data["side_effect"] + + if raises: + with pytest.raises(raises): + P.package_list() + else: + assert sorted(P.package_list()) == sorted(expected) + if run_command_data: + assert self.mock_run_command.mock_calls == run_command_data["calls"] + + @pytest.mark.parametrize("check_mode_value", [True, False]) + @pytest.mark.parametrize( + "name, state, package_list", + [ + (["already-absent"], "absent", [Package("already-absent", "already-absent")]), + (["grep"], "present", [Package("grep", "grep")]), + ], + ) + def test_op_packages_nothing_to_do( + self, mock_valid_inventory, mock_package_list, check_mode_value, name, state, package_list + ): + set_module_args({"name": name, "state": state, "_ansible_check_mode": check_mode_value}) + mock_package_list.return_value = package_list + P = pacman.Pacman(pacman.setup_module()) + with pytest.raises(AnsibleExitJson) as e: + P.run() + out = e.value.args[0] + assert not out["changed"] + assert "packages" in out + assert "diff" not in out + self.mock_run_command.call_count == 0 + + @pytest.mark.parametrize( + "module_args, expected_packages, package_list_out, run_command_data, raises", + [ + ( + # remove pkg: Check mode -- call to print format but that's it + {"_ansible_check_mode": True, "name": ["grep"], "state": "absent"}, + ["grep-version"], + [Package("grep", "grep")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--remove", + "--noconfirm", + "--noprogressbar", + "--print-format", + "%n-%v", + "grep", + ], + check_rc=False, + ), + ], + "side_effect": [(0, "grep-version", "")], + }, + AnsibleExitJson, + ), + ( + # remove pkg for real now -- with 2 packages + {"name": ["grep", "gawk"], "state": "absent"}, + ["grep-version", "gawk-anotherversion"], + [Package("grep", "grep"), Package("gawk", "gawk")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--remove", + "--noconfirm", + "--noprogressbar", + "--print-format", + "%n-%v", + "grep", + "gawk", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + ["pacman", "--remove", "--noconfirm", "--noprogressbar", "grep", "gawk"], + check_rc=False, + ), + ], + "side_effect": [ + (0, "grep-version\ngawk-anotherversion", ""), + (0, "stdout", "stderr"), + ], + }, + AnsibleExitJson, + ), + ( + # remove pkg force + extra_args + { + "name": ["grep"], + "state": "absent", + "force": True, + "extra_args": "--some --extra arg", + }, + ["grep-version"], + [Package("grep", "grep")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--remove", + "--noconfirm", + "--noprogressbar", + "--some", + "--extra", + "arg", + "--nodeps", + "--nodeps", + "--print-format", + "%n-%v", + "grep", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--remove", + "--noconfirm", + "--noprogressbar", + "--some", + "--extra", + "arg", + "--nodeps", + "--nodeps", + "grep", + ], + check_rc=False, + ), + ], + "side_effect": [ + (0, "grep-version", ""), + (0, "stdout", "stderr"), + ], + }, + AnsibleExitJson, + ), + ( + # remove pkg -- Failure to list + {"name": ["grep"], "state": "absent"}, + ["grep-3.7-1"], + [Package("grep", "grep")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--remove", + "--noconfirm", + "--noprogressbar", + "--print-format", + "%n-%v", + "grep", + ], + check_rc=False, + ) + ], + "side_effect": [ + (1, "stdout", "stderr"), + ], + }, + AnsibleFailJson, + ), + ( + # remove pkg -- Failure to remove + {"name": ["grep"], "state": "absent"}, + ["grep-3.7-1"], + [Package("grep", "grep")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--remove", + "--noconfirm", + "--noprogressbar", + "--print-format", + "%n-%v", + "grep", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + ["pacman", "--remove", "--noconfirm", "--noprogressbar", "grep"], + check_rc=False, + ), + ], + "side_effect": [ + (0, "grep", ""), + (1, "stdout", "stderr"), + ], + }, + AnsibleFailJson, + ), + ( + # install pkg: Check mode + {"_ansible_check_mode": True, "name": ["sudo"], "state": "present"}, + ["sudo"], + [Package("sudo", "sudo")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--sync", + "--print-format", + "%n %v", + "sudo", + ], + check_rc=False, + ), + ], + "side_effect": [(0, "sudo version", "")], + }, + AnsibleExitJson, + ), + ( + # Install pkgs: one regular, one already installed, one file URL and one https URL + { + "name": [ + "sudo", + "grep", + "./somepackage-12.3-x86_64.pkg.tar.zst", + "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst", + ], + "state": "present", + }, + ["otherpkg", "somepackage", "sudo"], + [ + Package("sudo", "sudo"), + Package("grep", "grep"), + Package("somepackage", "./somepackage-12.3-x86_64.pkg.tar.zst", source_is_URL=True), + Package( + "otherpkg", + "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst", + source_is_URL=True, + ), + ], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--sync", + "--print-format", + "%n %v", + "sudo", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--upgrade", + "--print-format", + "%n %v", + "./somepackage-12.3-x86_64.pkg.tar.zst", + "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--sync", + "sudo", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--upgrade", + "./somepackage-12.3-x86_64.pkg.tar.zst", + "http://example.com/otherpkg-1.2-x86_64.pkg.tar.zst", + ], + check_rc=False, + ), + ], + "side_effect": [ + (0, "sudo version", ""), + (0, "somepackage 12.3\notherpkg 1.2", ""), + (0, "", ""), + (0, "", ""), + ], + }, + AnsibleExitJson, + ), + ( + # install pkg, extra_args + {"name": ["sudo"], "state": "present", "extra_args": "--some --thing else"}, + ["sudo"], + [Package("sudo", "sudo")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--some", + "--thing", + "else", + "--sync", + "--print-format", + "%n %v", + "sudo", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--some", + "--thing", + "else", + "--sync", + "sudo", + ], + check_rc=False, + ), + ], + "side_effect": [(0, "sudo version", ""), (0, "", "")], + }, + AnsibleExitJson, + ), + ( + # latest pkg: Check mode + {"_ansible_check_mode": True, "name": ["sqlite"], "state": "latest"}, + ["sqlite"], + [Package("sqlite", "sqlite")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--sync", + "--print-format", + "%n %v", + "sqlite", + ], + check_rc=False, + ), + ], + "side_effect": [(0, "sqlite new-version", "")], + }, + AnsibleExitJson, + ), + ( + # latest pkg -- one already latest + {"name": ["sqlite", "grep"], "state": "latest"}, + ["sqlite"], + [Package("sqlite", "sqlite")], + { + "calls": [ + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--sync", + "--print-format", + "%n %v", + "sqlite", + ], + check_rc=False, + ), + mock.call( + mock.ANY, + [ + "pacman", + "--noconfirm", + "--noprogressbar", + "--needed", + "--sync", + "sqlite", + ], + check_rc=False, + ), + ], + "side_effect": [(0, "sqlite new-version", ""), (0, "", "")], + }, + AnsibleExitJson, + ), + ], + ) + def test_op_packages( + self, + mock_valid_inventory, + mock_package_list, + module_args, + expected_packages, + package_list_out, + run_command_data, + raises, + ): + set_module_args(module_args) + self.mock_run_command.side_effect = run_command_data["side_effect"] + mock_package_list.return_value = package_list_out + + P = pacman.Pacman(pacman.setup_module()) + with pytest.raises(raises) as e: + P.run() + out = e.value.args[0] + + assert self.mock_run_command.mock_calls == run_command_data["calls"] + if raises == AnsibleExitJson: + assert out["packages"] == expected_packages + assert out["changed"] + assert "packages" in out + assert "diff" in out + else: + assert out["stdout"] == "stdout" + assert out["stderr"] == "stderr" diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pacman_key.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pacman_key.py new file mode 100644 index 000000000..ac8570898 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pacman_key.py @@ -0,0 +1,577 @@ +# Copyright (c) 2019, George Rawlinson +# 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.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.modules import pacman_key +import pytest +import json + +# path used for mocking get_bin_path() +MOCK_BIN_PATH = '/mocked/path' + +# Key ID used for tests +TESTING_KEYID = '14F26682D0916CDD81E37B6D61B7B526D98F0353' +TESTING_KEYFILE_PATH = '/tmp/pubkey.asc' + +# gpg --{show,list}-key output (key present) +GPG_SHOWKEY_OUTPUT = '''tru::1:1616373715:0:3:1:5 +pub:-:4096:1:61B7B526D98F0353:1437155332:::-:::scSC::::::23::0: +fpr:::::::::14F26682D0916CDD81E37B6D61B7B526D98F0353: +uid:-::::1437155332::E57D1F9BFF3B404F9F30333629369B08DF5E2161::Mozilla Software Releases ::::::::::0: +sub:e:4096:1:1C69C4E55E9905DB:1437155572:1500227572:::::s::::::23: +fpr:::::::::F2EF4E6E6AE75B95F11F1EB51C69C4E55E9905DB: +sub:e:4096:1:BBBEBDBB24C6F355:1498143157:1561215157:::::s::::::23: +fpr:::::::::DCEAC5D96135B91C4EA672ABBBBEBDBB24C6F355: +sub:e:4096:1:F1A6668FBB7D572E:1559247338:1622319338:::::s::::::23: +fpr:::::::::097B313077AE62A02F84DA4DF1A6668FBB7D572E:''' + +# gpg --{show,list}-key output (key absent) +GPG_NOKEY_OUTPUT = '''gpg: error reading key: No public key +tru::1:1616373715:0:3:1:5''' + +# pacman-key output (successful invocation) +PACMAN_KEY_SUCCESS = '''==> Updating trust database... +gpg: next trustdb check due at 2021-08-02''' + +# expected command for gpg --list-keys KEYID +RUN_CMD_LISTKEYS = [ + MOCK_BIN_PATH, + '--with-colons', + '--batch', + '--no-tty', + '--no-default-keyring', + '--keyring=/etc/pacman.d/gnupg/pubring.gpg', + '--list-keys', + TESTING_KEYID, +] + +# expected command for gpg --show-keys KEYFILE +RUN_CMD_SHOW_KEYFILE = [ + MOCK_BIN_PATH, + '--with-colons', + '--with-fingerprint', + '--batch', + '--no-tty', + '--show-keys', + TESTING_KEYFILE_PATH, +] + +# expected command for pacman-key --lsign-key KEYID +RUN_CMD_LSIGN_KEY = [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--lsign-key', + TESTING_KEYID, +] + + +TESTCASES = [ + # + # invalid user input + # + # state: present, id: absent + [ + { + 'state': 'present', + }, + { + 'id': 'param_missing_id', + 'msg': 'missing required arguments: id', + 'failed': True, + }, + ], + # state: present, required parameters: missing + [ + { + 'state': 'present', + 'id': '0xDOESNTMATTER', + }, + { + 'id': 'param_missing_method', + 'msg': 'state is present but any of the following are missing: data, file, url, keyserver', + 'failed': True, + }, + ], + # state: present, id: invalid (not full-length) + [ + { + 'id': '0xDOESNTMATTER', + 'data': 'FAKEDATA', + }, + { + 'id': 'param_id_not_full', + 'msg': 'key ID is not full-length: DOESNTMATTER', + 'failed': True, + }, + ], + # state: present, id: invalid (not hexadecimal) + [ + { + 'state': 'present', + 'id': '01234567890ABCDE01234567890ABCDE1234567M', + 'data': 'FAKEDATA', + }, + { + 'id': 'param_id_not_hex', + 'msg': 'key ID is not hexadecimal: 01234567890ABCDE01234567890ABCDE1234567M', + 'failed': True, + }, + ], + # state: absent, id: absent + [ + { + 'state': 'absent', + }, + { + 'id': 'param_absent_state_missing_id', + 'msg': 'missing required arguments: id', + 'failed': True, + }, + ], + # + # check mode + # + # state & key present + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'FAKEDATA', + '_ansible_check_mode': True, + }, + { + 'id': 'checkmode_state_and_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ], + 'changed': False, + }, + ], + # state present, key absent + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'FAKEDATA', + '_ansible_check_mode': True, + }, + { + 'id': 'checkmode_state_present_key_absent', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ], + 'changed': True, + }, + ], + # state & key absent + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + '_ansible_check_mode': True, + }, + { + 'id': 'checkmode_state_and_key_absent', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ], + 'changed': False, + }, + ], + # state absent, key present + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + '_ansible_check_mode': True, + }, + { + 'id': 'check_mode_state_absent_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ], + 'changed': True, + }, + ], + # + # normal operation + # + # state & key present + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'FAKEDATA', + }, + { + 'id': 'state_and_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ], + 'changed': False, + }, + ], + # state absent, key present + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + }, + { + 'id': 'state_absent_key_present', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--delete', + TESTING_KEYID, + ], + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'changed': True, + }, + ], + # state & key absent + [ + { + 'state': 'absent', + 'id': TESTING_KEYID, + }, + { + 'id': 'state_and_key_absent', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ], + 'changed': False, + }, + ], + # state: present, key: absent, method: file + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'file': TESTING_KEYFILE_PATH, + }, + { + 'id': 'state_present_key_absent_method_file', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + RUN_CMD_SHOW_KEYFILE, + {'check_rc': True}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--add', + '/tmp/pubkey.asc', + ], + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ( + RUN_CMD_LSIGN_KEY, + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'changed': True, + }, + ], + # state: present, key: absent, method: file + # failure: keyid & keyfile don't match + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'file': TESTING_KEYFILE_PATH, + }, + { + 'id': 'state_present_key_absent_verify_failed', + 'msg': 'key ID does not match. expected 14F26682D0916CDD81E37B6D61B7B526D98F0353, got 14F26682D0916CDD81E37B6D61B7B526D98F0354', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + RUN_CMD_SHOW_KEYFILE, + {'check_rc': True}, + ( + 0, + GPG_SHOWKEY_OUTPUT.replace('61B7B526D98F0353', '61B7B526D98F0354'), + '', + ), + ), + ], + 'failed': True, + }, + ], + # state: present, key: absent, method: keyserver + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'keyserver': 'pgp.mit.edu', + }, + { + 'id': 'state_present_key_absent_method_keyserver', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--keyserver', + 'pgp.mit.edu', + '--recv-keys', + TESTING_KEYID, + ], + {'check_rc': True}, + ( + 0, + ''' +gpg: key 0x61B7B526D98F0353: 32 signatures not checked due to missing keys +gpg: key 0x61B7B526D98F0353: public key "Mozilla Software Releases " imported +gpg: marginals needed: 3 completes needed: 1 trust model: pgp +gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u +gpg: Total number processed: 1 +gpg: imported: 1 +''', + '', + ), + ), + ( + RUN_CMD_LSIGN_KEY, + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'changed': True, + }, + ], + # state: present, key: absent, method: data + [ + { + 'state': 'present', + 'id': TESTING_KEYID, + 'data': 'PGP_DATA', + }, + { + 'id': 'state_present_key_absent_method_data', + 'run_command.calls': [ + ( + RUN_CMD_LISTKEYS, + {'check_rc': False}, + ( + 2, + '', + GPG_NOKEY_OUTPUT, + ), + ), + ( + RUN_CMD_SHOW_KEYFILE, + {'check_rc': True}, + ( + 0, + GPG_SHOWKEY_OUTPUT, + '', + ), + ), + ( + [ + MOCK_BIN_PATH, + '--gpgdir', + '/etc/pacman.d/gnupg', + '--add', + '/tmp/pubkey.asc', + ], + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ( + RUN_CMD_LSIGN_KEY, + {'check_rc': True}, + ( + 0, + PACMAN_KEY_SUCCESS, + '', + ), + ), + ], + 'save_key_output': TESTING_KEYFILE_PATH, + 'changed': True, + }, + ], +] + + +@pytest.fixture +def patch_get_bin_path(mocker): + get_bin_path = mocker.patch.object( + AnsibleModule, + 'get_bin_path', + return_value=MOCK_BIN_PATH, + ) + + +@pytest.mark.parametrize( + 'patch_ansible_module, expected', + TESTCASES, + ids=[item[1]['id'] for item in TESTCASES], + indirect=['patch_ansible_module'] +) +@pytest.mark.usefixtures('patch_ansible_module') +def test_operation(mocker, capfd, patch_get_bin_path, expected): + # patch run_command invocations with mock data + if 'run_command.calls' in expected: + mock_run_command = mocker.patch.object( + AnsibleModule, + 'run_command', + side_effect=[item[2] for item in expected['run_command.calls']], + ) + + # patch save_key invocations with mock data + if 'save_key_output' in expected: + mock_save_key = mocker.patch.object( + pacman_key.PacmanKey, + 'save_key', + return_value=expected['save_key_output'], + ) + + # invoke module + with pytest.raises(SystemExit): + pacman_key.main() + + # capture std{out,err} + out, err = capfd.readouterr() + results = json.loads(out) + + # assertion time! + if 'msg' in expected: + assert results['msg'] == expected['msg'] + if 'changed' in expected: + assert results['changed'] == expected['changed'] + if 'failed' in expected: + assert results['failed'] == expected['failed'] + + if 'run_command.calls' in expected: + assert AnsibleModule.run_command.call_count == len(expected['run_command.calls']) + call_args_list = [(item[0][0], item[1]) for item in AnsibleModule.run_command.call_args_list] + expected_call_args_list = [(item[0], item[1]) for item in expected['run_command.calls']] + assert call_args_list == expected_call_args_list 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 new file mode 100644 index 000000000..d363804bc --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py @@ -0,0 +1,130 @@ +# 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.plugins.modules import pagerduty + +import json + + +class PagerDutyTest(unittest.TestCase): + def setUp(self): + self.pd = pagerduty.PagerDutyRequest(module=pagerduty, name='name', user='user', token='token') + + def _assert_ongoing_maintenance_windows(self, module, url, headers): + self.assertEqual('https://api.pagerduty.com/maintenance_windows?filter=ongoing', url) + 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, + 'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found' + ) + return object(), {'status': 200} + + def _assert_create_a_maintenance_window_url(self, module, url, headers, data=None, method=None): + self.assertEqual('https://api.pagerduty.com/maintenance_windows', url) + return object(), {'status': 201} + + def _assert_create_a_maintenance_window_http_method(self, module, url, headers, data=None, method=None): + self.assertEqual('POST', method) + 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, + '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, + 'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found' + ) + return object(), {'status': 201} + + 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') + return object(), {'status': 201} + + def _assert_create_window_single_service(self, module, url, headers, data=None, method=None): + payload = json.loads(data) + window_data = payload['maintenance_window'] + services = window_data['services'] + self.assertEqual( + [{'id': 'service_id', 'type': 'service_reference'}], + services + ) + return object(), {'status': 201} + + def _assert_create_window_multiple_service(self, module, url, headers, data=None, method=None): + payload = json.loads(data) + window_data = payload['maintenance_window'] + services = window_data['services'] + print(services) + self.assertEqual( + [ + {'id': 'service_id_1', 'type': 'service_reference'}, + {'id': 'service_id_2', 'type': 'service_reference'}, + {'id': 'service_id_3', 'type': 'service_reference'}, + ], + services + ) + return object(), {'status': 201} + + def _assert_absent_maintenance_window_url(self, module, url, headers, method=None): + self.assertEqual('https://api.pagerduty.com/maintenance_windows/window_id', url) + 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, + 'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found' + ) + return object(), {'status': 204} + + def test_ongoing_maintenance_windos_url(self): + self.pd.ongoing(http_call=self._assert_ongoing_maintenance_windows) + + def test_ongoing_maintenance_windos_compatibility_header(self): + self.pd.ongoing(http_call=self._assert_ongoing_window_with_v1_compatible_header) + + def test_create_maintenance_window_url(self): + self.pd.create('requester_id', 'service', 1, 0, 'desc', http_call=self._assert_create_a_maintenance_window_url) + + def test_create_maintenance_window_http_method(self): + self.pd.create('requester_id', 'service', 1, 0, 'desc', http_call=self._assert_create_a_maintenance_window_http_method) + + def test_create_maintenance_from_header(self): + self.pd.create('requester_id', 'service', 1, 0, 'desc', http_call=self._assert_create_a_maintenance_window_from_header) + + def test_create_maintenance_compatibility_header(self): + self.pd.create('requester_id', 'service', 1, 0, 'desc', http_call=self._assert_create_window_with_v1_compatible_header) + + def test_create_maintenance_request_payload(self): + self.pd.create('requester_id', 'service', 1, 0, 'desc', http_call=self._assert_create_window_payload) + + def test_create_maintenance_for_single_service(self): + self.pd.create('requester_id', 'service_id', 1, 0, 'desc', http_call=self._assert_create_window_single_service) + + def test_create_maintenance_for_multiple_services(self): + self.pd.create('requester_id', ['service_id_1', 'service_id_2', 'service_id_3'], 1, 0, 'desc', http_call=self._assert_create_window_multiple_service) + + def test_absent_maintenance_window_url(self): + self.pd.absent('window_id', http_call=self._assert_absent_maintenance_window_url) + + def test_absent_maintenance_compatibility_header(self): + self.pd.absent('window_id', http_call=self._assert_absent_window_with_v1_compatible_header) 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 new file mode 100644 index 000000000..3df992b42 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py @@ -0,0 +1,46 @@ +# 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.plugins.modules import pagerduty_alert + + +class PagerDutyAlertsTest(unittest.TestCase): + def _assert_incident_api(self, module, url, method, headers): + self.assertTrue('https://api.pagerduty.com/incidents' in url, 'url must contain REST API v2 network path') + self.assertTrue('service_ids%5B%5D=service_id' in url, 'url must contain service id to filter incidents') + self.assertTrue('sort_by=incident_number%3Adesc' in url, 'url should contain sorting parameter') + self.assertTrue('time_zone=UTC' in url, 'url should contain time zone parameter') + return Response(), {'status': 200} + + def _assert_compatibility_header(self, module, url, method, headers): + self.assertDictContainsSubset( + {'Accept': 'application/vnd.pagerduty+json;version=2'}, + headers, + 'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found' + ) + return Response(), {'status': 200} + + def _assert_incident_key(self, module, url, method, headers): + self.assertTrue('incident_key=incident_key_value' in url, 'url must contain incident key') + return Response(), {'status': 200} + + def test_incident_url(self): + pagerduty_alert.check(None, 'name', 'state', 'service_id', 'integration_key', 'api_key', http_call=self._assert_incident_api) + + def test_compatibility_header(self): + pagerduty_alert.check(None, 'name', 'state', 'service_id', 'integration_key', 'api_key', http_call=self._assert_compatibility_header) + + def test_incident_key_in_url_when_it_is_given(self): + pagerduty_alert.check( + None, 'name', 'state', 'service_id', 'integration_key', 'api_key', incident_key='incident_key_value', http_call=self._assert_incident_key + ) + + +class Response(object): + def read(self): + return '{"incidents":[{"id": "incident_id", "status": "triggered"}]}' diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_change.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_change.py new file mode 100644 index 000000000..d596d6ab8 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_change.py @@ -0,0 +1,85 @@ +# 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 +import pytest +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.plugins.modules import pagerduty_change +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class TestPagerDutyChangeModule(ModuleTestCase): + def setUp(self): + super(TestPagerDutyChangeModule, self).setUp() + self.module = pagerduty_change + + def tearDown(self): + super(TestPagerDutyChangeModule, 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_change_event_created_with_minimal_data(self): + set_module_args({ + 'integration_key': 'test', + 'summary': 'Testing' + }) + + with patch.object(pagerduty_change, 'fetch_url') as fetch_url_mock: + fetch_url_mock.return_value = (None, {"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/change/enqueue' + assert data['routing_key'] == 'test' + assert data['payload']['summary'] == 'Testing' + assert data['payload']['source'] == 'Ansible' + + def test_ensure_change_event_created_with_full_data(self): + set_module_args({ + 'integration_key': 'test', + 'summary': 'Testing', + 'source': 'My Ansible Script', + 'user': 'ansible', + 'repo': 'github.com/ansible/ansible', + 'revision': '8c67432', + 'environment': 'production', + 'link_url': 'https://pagerduty.com', + 'link_text': 'PagerDuty' + }) + + with patch.object(pagerduty_change, 'fetch_url') as fetch_url_mock: + fetch_url_mock.return_value = (None, {"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/change/enqueue' + assert data['routing_key'] == 'test' + assert data['payload']['summary'] == 'Testing' + assert data['payload']['source'] == 'My Ansible Script' + assert data['payload']['custom_details']['user'] == 'ansible' + assert data['payload']['custom_details']['repo'] == 'github.com/ansible/ansible' + assert data['payload']['custom_details']['revision'] == '8c67432' + assert data['payload']['custom_details']['environment'] == 'production' + assert data['links'][0]['href'] == 'https://pagerduty.com' + assert data['links'][0]['text'] == 'PagerDuty' diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pamd.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pamd.py new file mode 100644 index 000000000..4c49cebed --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pamd.py @@ -0,0 +1,386 @@ +# 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.plugins.modules.pamd import PamdRule +from ansible_collections.community.general.plugins.modules.pamd import PamdLine +from ansible_collections.community.general.plugins.modules.pamd import PamdComment +from ansible_collections.community.general.plugins.modules.pamd import PamdInclude +from ansible_collections.community.general.plugins.modules.pamd import PamdService + + +class PamdLineTestCase(unittest.TestCase): + + def setUp(self): + self.pamd_line = PamdLine("This is a test") + + def test_line(self): + self.assertEqual("This is a test", str(self.pamd_line)) + + def test_matches(self): + self.assertFalse(self.pamd_line.matches("test", "matches", "foo", "bar")) + + +class PamdIncludeTestCase(unittest.TestCase): + + def setUp(self): + self.good_include = PamdInclude("@include foobar") + self.bad_include = PamdInclude("include foobar") + + def test_line(self): + self.assertEqual("@include foobar", str(self.good_include)) + + def test_matches(self): + self.assertFalse(self.good_include.matches("something", "something", "dark", "side")) + + def test_valid(self): + self.assertTrue(self.good_include.is_valid) + self.assertFalse(self.bad_include.is_valid) + + +class PamdCommentTestCase(unittest.TestCase): + + def setUp(self): + self.good_comment = PamdComment("# This is a test comment") + self.bad_comment = PamdComment("This is a bad test comment") + + def test_line(self): + self.assertEqual("# This is a test comment", str(self.good_comment)) + + def test_matches(self): + self.assertFalse(self.good_comment.matches("test", "matches", "foo", "bar")) + + def test_valid(self): + self.assertTrue(self.good_comment.is_valid) + self.assertFalse(self.bad_comment.is_valid) + + +class PamdRuleTestCase(unittest.TestCase): + def setUp(self): + self.rule = PamdRule('account', 'optional', 'pam_keyinit.so', 'revoke') + + def test_type(self): + self.assertEqual(self.rule.rule_type, 'account') + + def test_control(self): + self.assertEqual(self.rule.rule_control, 'optional') + self.assertEqual(self.rule._control, 'optional') + + def test_path(self): + self.assertEqual(self.rule.rule_path, 'pam_keyinit.so') + + def test_args(self): + self.assertEqual(self.rule.rule_args, ['revoke']) + + def test_valid(self): + self.assertTrue(self.rule.validate()[0]) + + +class PamdRuleBadValidationTestCase(unittest.TestCase): + def setUp(self): + self.bad_type = PamdRule('foobar', 'optional', 'pam_keyinit.so', 'revoke') + self.bad_control_simple = PamdRule('account', 'foobar', 'pam_keyinit.so', 'revoke') + self.bad_control_value = PamdRule('account', '[foobar=1 default=ignore]', 'pam_keyinit.so', 'revoke') + self.bad_control_action = PamdRule('account', '[success=1 default=foobar]', 'pam_keyinit.so', 'revoke') + + def test_validate_bad_type(self): + self.assertFalse(self.bad_type.validate()[0]) + + def test_validate_bad_control_simple(self): + self.assertFalse(self.bad_control_simple.validate()[0]) + + def test_validate_bad_control_value(self): + self.assertFalse(self.bad_control_value.validate()[0]) + + def test_validate_bad_control_action(self): + self.assertFalse(self.bad_control_action.validate()[0]) + + +class PamdServiceTestCase(unittest.TestCase): + def setUp(self): + self.system_auth_string = """#%PAM-1.0 +# This file is auto-generated. +# User changes will be destroyed the next time authconfig is run. +@include common-auth +@include common-account +@include common-session +auth required pam_env.so +auth sufficient pam_unix.so nullok try_first_pass +auth requisite pam_succeed_if.so uid +auth required pam_deny.so +# Test comment +auth sufficient pam_rootok.so + +account required pam_unix.so +account sufficient pam_localuser.so +account sufficient pam_succeed_if.so uid +account [success=1 default=ignore] \ + pam_succeed_if.so user = vagrant use_uid quiet +account required pam_permit.so +account required pam_access.so listsep=, +session include system-auth + +password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type= +password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok +password required pam_deny.so + +session optional pam_keyinit.so revoke +session required pam_limits.so +-session optional pam_systemd.so +session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid +session [success=1 test=me default=ignore] pam_succeed_if.so service in crond quiet use_uid +session required pam_unix.so""" + + self.simple_system_auth_string = """#%PAM-1.0 + auth required pam_env.so +""" + + self.no_header_system_auth_string = """auth required pam_env.so +auth sufficient pam_unix.so nullok try_first_pass +auth requisite pam_succeed_if.so uid +auth required pam_deny.so +""" + + self.pamd = PamdService(self.system_auth_string) + + def test_properly_parsed(self): + num_lines = len(self.system_auth_string.splitlines()) + 1 + num_lines_processed = len(str(self.pamd).splitlines()) + self.assertEqual(num_lines, num_lines_processed) + + def test_has_rule(self): + self.assertTrue(self.pamd.has_rule('account', 'required', 'pam_permit.so')) + self.assertTrue(self.pamd.has_rule('account', '[success=1 default=ignore]', 'pam_succeed_if.so')) + + def test_doesnt_have_rule(self): + self.assertFalse(self.pamd.has_rule('account', 'requisite', 'pam_permit.so')) + + # Test Update + def test_update_rule_type(self): + self.assertTrue(self.pamd.update_rule('session', 'optional', 'pam_keyinit.so', new_type='account')) + self.assertTrue(self.pamd.has_rule('account', 'optional', 'pam_keyinit.so')) + test_rule = PamdRule('account', 'optional', 'pam_keyinit.so', 'revoke') + self.assertIn(str(test_rule), str(self.pamd)) + + def test_update_rule_that_doesnt_exist(self): + self.assertFalse(self.pamd.update_rule('blah', 'blah', 'blah', new_type='account')) + self.assertFalse(self.pamd.has_rule('blah', 'blah', 'blah')) + test_rule = PamdRule('blah', 'blah', 'blah', 'account') + self.assertNotIn(str(test_rule), str(self.pamd)) + + def test_update_rule_type_two(self): + self.assertTrue(self.pamd.update_rule('session', '[success=1 default=ignore]', 'pam_succeed_if.so', new_type='account')) + self.assertTrue(self.pamd.has_rule('account', '[success=1 default=ignore]', 'pam_succeed_if.so')) + test_rule = PamdRule('account', '[success=1 default=ignore]', 'pam_succeed_if.so') + self.assertIn(str(test_rule), str(self.pamd)) + + def test_update_rule_control_simple(self): + self.assertTrue(self.pamd.update_rule('session', 'optional', 'pam_keyinit.so', new_control='required')) + self.assertTrue(self.pamd.has_rule('session', 'required', 'pam_keyinit.so')) + test_rule = PamdRule('session', 'required', 'pam_keyinit.so') + self.assertIn(str(test_rule), str(self.pamd)) + + def test_update_rule_control_complex(self): + self.assertTrue(self.pamd.update_rule('session', + '[success=1 default=ignore]', + 'pam_succeed_if.so', + new_control='[success=2 test=me default=ignore]')) + self.assertTrue(self.pamd.has_rule('session', '[success=2 test=me default=ignore]', 'pam_succeed_if.so')) + test_rule = PamdRule('session', '[success=2 test=me default=ignore]', 'pam_succeed_if.so') + self.assertIn(str(test_rule), str(self.pamd)) + + def test_update_rule_control_more_complex(self): + + self.assertTrue(self.pamd.update_rule('session', + '[success=1 test=me default=ignore]', + 'pam_succeed_if.so', + new_control='[success=2 test=me default=ignore]')) + self.assertTrue(self.pamd.has_rule('session', '[success=2 test=me default=ignore]', 'pam_succeed_if.so')) + test_rule = PamdRule('session', '[success=2 test=me default=ignore]', 'pam_succeed_if.so') + self.assertIn(str(test_rule), str(self.pamd)) + + def test_update_rule_module_path(self): + self.assertTrue(self.pamd.update_rule('auth', 'required', 'pam_env.so', new_path='pam_limits.so')) + self.assertTrue(self.pamd.has_rule('auth', 'required', 'pam_limits.so')) + + def test_update_rule_module_path_slash(self): + self.assertTrue(self.pamd.update_rule('auth', 'required', 'pam_env.so', new_path='/lib64/security/pam_duo.so')) + self.assertTrue(self.pamd.has_rule('auth', 'required', '/lib64/security/pam_duo.so')) + + def test_update_rule_module_args(self): + self.assertTrue(self.pamd.update_rule('auth', 'sufficient', 'pam_unix.so', new_args='uid uid')) + test_rule = PamdRule('auth', 'sufficient', 'pam_unix.so', 'uid uid') + self.assertIn(str(test_rule), str(self.pamd)) + + test_rule = PamdRule('auth', 'sufficient', 'pam_unix.so', 'nullok try_first_pass') + self.assertNotIn(str(test_rule), str(self.pamd)) + + def test_update_rule_remove_module_args(self): + self.assertTrue(self.pamd.update_rule('auth', 'sufficient', 'pam_unix.so', new_args='')) + test_rule = PamdRule('auth', 'sufficient', 'pam_unix.so', '') + self.assertIn(str(test_rule), str(self.pamd)) + + test_rule = PamdRule('auth', 'sufficient', 'pam_unix.so', 'nullok try_first_pass') + self.assertNotIn(str(test_rule), str(self.pamd)) + + def test_update_first_three(self): + self.assertTrue(self.pamd.update_rule('auth', 'required', 'pam_env.so', + new_type='one', new_control='two', new_path='three')) + self.assertTrue(self.pamd.has_rule('one', 'two', 'three')) + + def test_update_first_three_with_module_args(self): + self.assertTrue(self.pamd.update_rule('auth', 'sufficient', 'pam_unix.so', + new_type='one', new_control='two', new_path='three')) + self.assertTrue(self.pamd.has_rule('one', 'two', 'three')) + test_rule = PamdRule('one', 'two', 'three') + self.assertIn(str(test_rule), str(self.pamd)) + self.assertIn(str(test_rule), str(self.pamd)) + + def test_update_all_four(self): + self.assertTrue(self.pamd.update_rule('auth', 'sufficient', 'pam_unix.so', + new_type='one', new_control='two', new_path='three', + new_args='four five')) + test_rule = PamdRule('one', 'two', 'three', 'four five') + self.assertIn(str(test_rule), str(self.pamd)) + + test_rule = PamdRule('auth', 'sufficient', 'pam_unix.so', 'nullok try_first_pass') + self.assertNotIn(str(test_rule), str(self.pamd)) + + def test_update_rule_with_slash(self): + self.assertTrue(self.pamd.update_rule('account', '[success=1 default=ignore]', 'pam_succeed_if.so', + new_type='session', new_path='pam_access.so')) + test_rule = PamdRule('session', '[success=1 default=ignore]', 'pam_access.so') + self.assertIn(str(test_rule), str(self.pamd)) + + # Insert Before + def test_insert_before_rule(self): + + count = self.pamd.insert_before('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_limits.so') + self.assertEqual(count, 1) + + rules = self.pamd.get("account", "required", "pam_access.so") + for current_rule in rules: + self.assertTrue(current_rule.prev.matches("account", "required", "pam_limits.so")) + + def test_insert_before_rule_where_rule_doesnt_exist(self): + + count = self.pamd.insert_before('account', 'sufficient', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_limits.so') + self.assertFalse(count) + + def test_insert_before_rule_with_args(self): + self.assertTrue(self.pamd.insert_before('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_limits.so', + new_args='uid')) + + rules = self.pamd.get("account", "required", "pam_access.so") + for current_rule in rules: + self.assertTrue(current_rule.prev.matches("account", "required", "pam_limits.so", 'uid')) + + def test_insert_before_rule_test_duplicates(self): + self.assertTrue(self.pamd.insert_before('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_limits.so')) + + self.pamd.insert_before('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_limits.so') + + rules = self.pamd.get("account", "required", "pam_access.so") + for current_rule in rules: + previous_rule = current_rule.prev + self.assertTrue(previous_rule.matches("account", "required", "pam_limits.so")) + self.assertFalse(previous_rule.prev.matches("account", "required", "pam_limits.so")) + + def test_insert_before_first_rule(self): + self.assertTrue(self.pamd.insert_before('auth', 'required', 'pam_env.so', + new_type='account', new_control='required', new_path='pam_limits.so')) + + def test_insert_before_first_rule_simple(self): + simple_service = PamdService(self.simple_system_auth_string) + self.assertTrue(simple_service.insert_before('auth', 'required', 'pam_env.so', + new_type='account', new_control='required', new_path='pam_limits.so')) + + # Insert After + def test_insert_after_rule(self): + self.assertTrue(self.pamd.insert_after('account', 'required', 'pam_unix.so', + new_type='account', new_control='required', new_path='pam_permit.so')) + rules = self.pamd.get("account", "required", "pam_unix.so") + for current_rule in rules: + self.assertTrue(current_rule.next.matches("account", "required", "pam_permit.so")) + + def test_insert_after_rule_with_args(self): + self.assertTrue(self.pamd.insert_after('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_permit.so', + new_args='uid')) + rules = self.pamd.get("account", "required", "pam_access.so") + for current_rule in rules: + self.assertTrue(current_rule.next.matches("account", "required", "pam_permit.so", "uid")) + + def test_insert_after_test_duplicates(self): + self.assertTrue(self.pamd.insert_after('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_permit.so', + new_args='uid')) + self.assertFalse(self.pamd.insert_after('account', 'required', 'pam_access.so', + new_type='account', new_control='required', new_path='pam_permit.so', + new_args='uid')) + + rules = self.pamd.get("account", "required", "pam_access.so") + for current_rule in rules: + self.assertTrue(current_rule.next.matches("account", "required", "pam_permit.so", "uid")) + self.assertFalse(current_rule.next.next.matches("account", "required", "pam_permit.so", "uid")) + + def test_insert_after_rule_last_rule(self): + self.assertTrue(self.pamd.insert_after('session', 'required', 'pam_unix.so', + new_type='account', new_control='required', new_path='pam_permit.so', + new_args='uid')) + rules = self.pamd.get("session", "required", "pam_unix.so") + for current_rule in rules: + self.assertTrue(current_rule.next.matches("account", "required", "pam_permit.so", "uid")) + + # Remove Module Arguments + def test_remove_module_arguments_one(self): + self.assertTrue(self.pamd.remove_module_arguments('auth', 'sufficient', 'pam_unix.so', 'nullok')) + + def test_remove_module_arguments_one_list(self): + self.assertTrue(self.pamd.remove_module_arguments('auth', 'sufficient', 'pam_unix.so', ['nullok'])) + + def test_remove_module_arguments_two(self): + self.assertTrue(self.pamd.remove_module_arguments('session', '[success=1 default=ignore]', 'pam_succeed_if.so', 'service crond')) + + def test_remove_module_arguments_two_list(self): + self.assertTrue(self.pamd.remove_module_arguments('session', '[success=1 default=ignore]', 'pam_succeed_if.so', ['service', 'crond'])) + + def test_remove_module_arguments_where_none_existed(self): + self.assertTrue(self.pamd.add_module_arguments('session', 'required', 'pam_limits.so', 'arg1 arg2= arg3=arg3')) + + def test_add_module_arguments_where_none_existed(self): + self.assertTrue(self.pamd.add_module_arguments('account', 'required', 'pam_unix.so', 'arg1 arg2= arg3=arg3')) + + def test_add_module_arguments_where_none_existed_list(self): + self.assertTrue(self.pamd.add_module_arguments('account', 'required', 'pam_unix.so', ['arg1', 'arg2=', 'arg3=arg3'])) + + def test_add_module_arguments_where_some_existed(self): + self.assertTrue(self.pamd.add_module_arguments('auth', 'sufficient', 'pam_unix.so', 'arg1 arg2= arg3=arg3')) + + def test_remove_rule(self): + self.assertTrue(self.pamd.remove('account', 'required', 'pam_unix.so')) + # Second run should not change anything + self.assertFalse(self.pamd.remove('account', 'required', 'pam_unix.so')) + test_rule = PamdRule('account', 'required', 'pam_unix.so') + self.assertNotIn(str(test_rule), str(self.pamd)) + + def test_remove_first_rule(self): + no_header_service = PamdService(self.no_header_system_auth_string) + self.assertTrue(no_header_service.remove('auth', 'required', 'pam_env.so')) + test_rule = PamdRule('auth', 'required', 'pam_env.so') + self.assertNotIn(str(test_rule), str(no_header_service)) + + def test_remove_last_rule(self): + self.assertTrue(self.pamd.remove('session', 'required', 'pam_unix.so')) + test_rule = PamdRule('session', 'required', 'pam_unix.so') + self.assertNotIn(str(test_rule), str(self.pamd)) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_parted.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_parted.py new file mode 100644 index 000000000..1e010343b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_parted.py @@ -0,0 +1,346 @@ +# (c) 2017 Red Hat Inc. +# 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 parted as parted_module +from ansible_collections.community.general.plugins.modules.parted import parse_parted_version +from ansible_collections.community.general.plugins.modules.parted import parse_partition_info +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + +# Example of output : parted -s -m /dev/sdb -- unit 'MB' print +parted_output1 = """ +BYT; +/dev/sdb:286061MB:scsi:512:512:msdos:ATA TOSHIBA THNSFJ25:; +1:1.05MB:106MB:105MB:fat32::esp; +2:106MB:368MB:262MB:ext2::; +3:368MB:256061MB:255692MB:::;""" + +parted_version_info = {""" + parted (GNU parted) 3.3 + Copyright (C) 2019 Free Software Foundation, Inc. + License GPLv3+: GNU GPL version 3 or later . + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Written by . + """: (3, 3, 0), """ + parted (GNU parted) 3.4.5 + Copyright (C) 2019 Free Software Foundation, Inc. + License GPLv3+: GNU GPL version 3 or later . + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Written by . + """: (3, 4, 5), """ + parted (GNU parted) 3.3.14-dfc61 + Copyright (C) 2019 Free Software Foundation, Inc. + License GPLv3+: GNU GPL version 3 or later . + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Written by . + """: (3, 3, 14)} + +# corresponding dictionary after parsing by parse_partition_info +parted_dict1 = { + "generic": { + "dev": "/dev/sdb", + "size": 286061.0, + "unit": "mb", + "table": "msdos", + "model": "ATA TOSHIBA THNSFJ25", + "logical_block": 512, + "physical_block": 512 + }, + "partitions": [{ + "num": 1, + "begin": 1.05, + "end": 106.0, + "size": 105.0, + "fstype": "fat32", + "name": '', + "flags": ["esp"], + "unit": "mb" + }, { + "num": 2, + "begin": 106.0, + "end": 368.0, + "size": 262.0, + "fstype": "ext2", + "name": '', + "flags": [], + "unit": "mb" + }, { + "num": 3, + "begin": 368.0, + "end": 256061.0, + "size": 255692.0, + "fstype": "", + "name": '', + "flags": [], + "unit": "mb" + }] +} + +parted_output2 = """ +BYT; +/dev/sdb:286061MB:scsi:512:512:msdos:ATA TOSHIBA THNSFJ25:;""" + +# corresponding dictionary after parsing by parse_partition_info +parted_dict2 = { + "generic": { + "dev": "/dev/sdb", + "size": 286061.0, + "unit": "mb", + "table": "msdos", + "model": "ATA TOSHIBA THNSFJ25", + "logical_block": 512, + "physical_block": 512 + }, + "partitions": [] +} + +# fake some_flag exists +parted_dict3 = { + "generic": { + "dev": "/dev/sdb", + "size": 286061.0, + "unit": "mb", + "table": "msdos", + "model": "ATA TOSHIBA THNSFJ25", + "logical_block": 512, + "physical_block": 512 + }, + "partitions": [{ + "num": 1, + "begin": 1.05, + "end": 106.0, + "size": 105.0, + "fstype": "fat32", + "name": '', + "flags": ["some_flag"], + "unit": "mb" + }] +} + + +class TestParted(ModuleTestCase): + def setUp(self): + super(TestParted, self).setUp() + + self.module = parted_module + self.mock_check_parted_label = (patch('ansible_collections.community.general.plugins.modules.parted.check_parted_label', return_value=False)) + self.check_parted_label = self.mock_check_parted_label.start() + + self.mock_parted = (patch('ansible_collections.community.general.plugins.modules.parted.parted')) + self.parted = self.mock_parted.start() + + self.mock_run_command = (patch('ansible.module_utils.basic.AnsibleModule.run_command')) + self.run_command = self.mock_run_command.start() + + self.mock_get_bin_path = (patch('ansible.module_utils.basic.AnsibleModule.get_bin_path')) + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + super(TestParted, self).tearDown() + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + self.mock_parted.stop() + self.mock_check_parted_label.stop() + + def execute_module(self, failed=False, changed=False, script=None): + if failed: + result = self.failed() + self.assertTrue(result['failed'], result) + else: + result = self.changed(changed) + self.assertEqual(result['changed'], changed, result) + + if script: + self.assertEqual(script, result['script'], result['script']) + + return result + + def failed(self): + with self.assertRaises(AnsibleFailJson) as exc: + self.module.main() + + result = exc.exception.args[0] + self.assertTrue(result['failed'], result) + 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, result) + return result + + def test_parse_partition_info(self): + """Test that the parse_partition_info returns the expected dictionary""" + self.assertEqual(parse_partition_info(parted_output1, 'MB'), parted_dict1) + self.assertEqual(parse_partition_info(parted_output2, 'MB'), parted_dict2) + + def test_partition_already_exists(self): + set_module_args({ + 'device': '/dev/sdb', + 'number': 1, + 'state': 'present', + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=False) + + def test_create_new_partition(self): + set_module_args({ + 'device': '/dev/sdb', + 'number': 4, + 'state': 'present', + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='unit KiB mkpart primary 0% 100%') + + def test_create_new_partition_1G(self): + set_module_args({ + 'device': '/dev/sdb', + 'number': 4, + 'state': 'present', + 'part_end': '1GiB', + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='unit KiB mkpart primary 0% 1GiB') + + def test_create_new_partition_minus_1G(self): + set_module_args({ + 'device': '/dev/sdb', + 'number': 4, + 'state': 'present', + 'fs_type': 'ext2', + 'part_start': '-1GiB', + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='unit KiB mkpart primary ext2 -1GiB 100%') + + def test_remove_partition_number_1(self): + set_module_args({ + 'device': '/dev/sdb', + 'number': 1, + 'state': 'absent', + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='rm 1') + + def test_resize_partition(self): + set_module_args({ + 'device': '/dev/sdb', + 'number': 3, + 'state': 'present', + 'part_end': '100%', + 'resize': True + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='resizepart 3 100%') + + def test_change_flag(self): + # Flags are set in a second run of parted(). + # Between the two runs, the partition dict is updated. + # use checkmode here allow us to continue even if the dictionary is + # not updated. + set_module_args({ + 'device': '/dev/sdb', + 'number': 3, + 'state': 'present', + 'flags': ['lvm', 'boot'], + '_ansible_check_mode': True, + }) + + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.parted.reset_mock() + self.execute_module(changed=True) + # When using multiple flags: + # order of execution is non deterministic, because set() operations are used in + # the current implementation. + expected_calls_order1 = [call('unit KiB set 3 lvm on set 3 boot on ', + '/dev/sdb', 'optimal')] + expected_calls_order2 = [call('unit KiB set 3 boot on set 3 lvm on ', + '/dev/sdb', 'optimal')] + self.assertTrue(self.parted.mock_calls == expected_calls_order1 or + self.parted.mock_calls == expected_calls_order2) + + def test_create_new_primary_lvm_partition(self): + # use check_mode, see previous test comment + set_module_args({ + 'device': '/dev/sdb', + 'number': 4, + 'flags': ["boot"], + 'state': 'present', + 'part_start': '257GiB', + 'fs_type': 'ext3', + '_ansible_check_mode': True, + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='unit KiB mkpart primary ext3 257GiB 100% unit KiB set 4 boot on') + + def test_create_label_gpt(self): + # Like previous test, current implementation use parted to create the partition and + # then retrieve and update the dictionary. Use check_mode to force to continue even if + # dictionary is not updated. + set_module_args({ + 'device': '/dev/sdb', + 'number': 1, + 'flags': ["lvm"], + 'label': 'gpt', + 'name': 'lvmpartition', + 'state': 'present', + '_ansible_check_mode': True, + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict2): + self.execute_module(changed=True, script='unit KiB mklabel gpt mkpart primary 0% 100% unit KiB name 1 \'"lvmpartition"\' set 1 lvm on') + + def test_change_label_gpt(self): + # When partitions already exists and label is changed, mkpart should be called even when partition already exists, + # because new empty label will be created anyway + set_module_args({ + 'device': '/dev/sdb', + 'number': 1, + 'state': 'present', + 'label': 'gpt', + '_ansible_check_mode': True, + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict1): + self.execute_module(changed=True, script='unit KiB mklabel gpt mkpart primary 0% 100%') + + def test_check_mode_unchanged(self): + # Test that get_device_info result is checked in check mode too + # No change on partition 1 + set_module_args({ + 'device': '/dev/sdb', + 'number': 1, + 'state': 'present', + 'flags': ['some_flag'], + '_ansible_check_mode': True, + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict3): + self.execute_module(changed=False) + + def test_check_mode_changed(self): + # Test that get_device_info result is checked in check mode too + # Flag change on partition 1 + set_module_args({ + 'device': '/dev/sdb', + 'number': 1, + 'state': 'present', + 'flags': ['other_flag'], + '_ansible_check_mode': True, + }) + with patch('ansible_collections.community.general.plugins.modules.parted.get_device_info', return_value=parted_dict3): + self.execute_module(changed=True) + + def test_version_info(self): + """Test that the parse_parted_version returns the expected tuple""" + for key, value in parted_version_info.items(): + self.assertEqual(parse_parted_version(key), value) 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 new file mode 100644 index 000000000..d73911e0c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py @@ -0,0 +1,145 @@ +# 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 mock +from ansible_collections.community.general.tests.unit.compat import unittest + +from ansible_collections.community.general.plugins.modules import pkgin + + +class TestPkginQueryPackage(unittest.TestCase): + + def setUp(self): + pkgin.PKGIN_PATH = "" + + @mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule') + def test_package_without_version_is_present(self, mock_module): + # given + package = 'py37-conan' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "%s-1.21.0 = C/C++ package manager" % package, None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(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): + # given + package = 'py37-conan-1.21.0' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "%s = C/C++ package manager" % package, None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(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): + # given + package = 'cmake' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "cmake316-3.16.0nb1 = Cross platform make\ncmake314-3.14.6nb1 = Cross platform make\ncmake-3.14.0 Cross platform make", None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(command_result, pkgin.PackageState.NOT_INSTALLED) + + @mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule') + def test_package_found_outdated(self, mock_module): + # given + package = 'cmake316' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "cmake316-3.16.0nb1 < Cross platform make", None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(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): + # given + package = 'cmake316-3.16.0nb1' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "cmake316-3.16.0nb1 < Cross platform make", None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(command_result, pkgin.PackageState.OUTDATED) + + @mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule') + def test_package_not_found(self, mock_module): + # given + package = 'cmake320-3.20.0nb1' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (1, None, "No results found for %s" % package), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(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): + # given + package = 'py37-conan' + parseable_flag_supported = 0 + mock_module.run_command.side_effect = [ + (parseable_flag_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "%s-1.21.0;=;C/C++ package manager" % package, None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(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): + # given + package = 'py37-conan' + parseable_flag_not_supported = 1 + mock_module.run_command.side_effect = [ + (parseable_flag_not_supported, "pkgin 0.11.7 for Darwin-18.6.0 x86_64 (using SQLite 3.27.2)", None), + (0, "%s-1.21.0 = C/C++ package manager" % package, None), + ] + + # when + command_result = pkgin.query_package(mock_module, package) + + # then + self.assertEquals(command_result, pkgin.PackageState.PRESENT) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pmem.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pmem.py new file mode 100644 index 000000000..cea673da0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pmem.py @@ -0,0 +1,707 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022, Masayoshi Mizuma +# 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 json + +pytest.importorskip('xmltodict') + +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ModuleTestCase, set_module_args, AnsibleFailJson, AnsibleExitJson +from ansible_collections.community.general.tests.unit.compat.mock import patch + +from ansible_collections.community.general.plugins.modules import pmem as pmem_module + +# goal_plain: the mock return value of pmem_run_command with: +# impctl create -goal MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect +goal_plain = """The following configuration will be applied: +SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size +================================================================== +0x0000 | 0x0001 | 88.000 GiB | 12.000 GiB | 0.000 GiB +0x0000 | 0x0011 | 88.000 GiB | 12.000 GiB | 0.000 GiB +0x0001 | 0x1001 | 88.000 GiB | 12.000 GiB | 0.000 GiB +0x0001 | 0x1011 | 88.000 GiB | 12.000 GiB | 0.000 GiB +Do you want to continue? [y/n] Error: Invalid data input.""" + +# goal_plain_sk0: the mock return value of pmem_run_command with: +# ipmctl create -goal -socket 0 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect +goal_plain_sk0 = """The following configuration will be applied: +SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size +================================================================== +0x0000 | 0x0001 | 88.000 GiB | 12.000 GiB | 0.000 GiB +0x0000 | 0x0011 | 88.000 GiB | 12.000 GiB | 0.000 GiB +Do you want to continue? [y/n] Error: Invalid data input.""" + +# goal_plain_sk1: the mock return value of pmem_run_command with: +# ipmctl create -goal -socket 1 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect +goal_plain_sk1 = """The following configuration will be applied: +SocketID | DimmID | MemorySize | AppDirect1Size | AppDirect2Size +================================================================== +0x0001 | 0x1001 | 88.000 GiB | 12.000 GiB | 0.000 GiB +0x0001 | 0x1011 | 88.000 GiB | 12.000 GiB | 0.000 GiB +Do you want to continue? [y/n] Error: Invalid data input.""" + +# goal: the mock return value of pmem_run_command with: +# ipmctl create -u B -o nvmxml -force -goal -socket 0 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect +goal = """ + + + 0x0000 + 0x0001 + 94489280512 B + 12884901888 B + 0 B + + + 0x0000 + 0x0011 + 94489280512 B + 12884901888 B + 0 B + +""" + +# goal_sk0: the mock return value of pmem_run_command with: +# ipmctl create -u B -o nvmxml -force -goal -socket 0 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect +goal_sk0 = """ + + + 0x0000 + 0x0001 + 94489280512 B + 12884901888 B + 0 B + + + 0x0001 + 0x0011 + 94489280512 B + 12884901888 B + 0 B + +""" + +# goal_sk1: the mock return value of pmem_run_command with: +# ipmctl create -u B -o nvmxml -force -goal -socket 1 MemoryMode=70 Reserved=20 PersistentMemoryType=AppDirect +goal_sk1 = """ + + + 0x0001 + 0x1001 + 94489280512 B + 12884901888 B + 0 B + + + 0x0001 + 0x1011 + 94489280512 B + 12884901888 B + 0 B + +""" + +# dimmlist: the mock return value of pmem_run_command with: +# ipmctl show -d Capacity -u B -o nvmxml -dimm +dimmlist = """ + + + 0x0001 + 135744782336 B + + 0x0011 + 135744782336 B + + + 0x1001 + 135744782336 B + + 0x1011 + 135744782336 B + +""" + +# dimmlist_sk0: the mock return value of pmem_run_command with: +# ipmctl show -d Capacity -u B -o nvmxml -dimm -socket 0 +dimmlist_sk0 = """ + + + 0x0001 + 135744782336 B + + 0x0011 + 135744782336 B + +""" + +# dimmlist_sk1: the mock return value of pmem_run_command with: +# ipmctl show -d Capacity -u B -o nvmxml -dimm -socket 1 +dimmlist_sk1 = """ + + + 0x1001 + 135744782336 B + + 0x1011 + 135744782336 B + +""" + +# show_skt: the mock return value of pmem_run_command with: +# ipmctl show -o nvmxml -socket +show_skt = """ + + + 0x0000 + 1024.000 GiB + 400.000 GiB + + + 0x0001 + 1024.000 GiB + 400.000 GiB + +""" + +# ndctl_region: the mock return value of pmem_run_command with: +# ndctl list -R +ndctl_region = """[ + { + "dev":"region1", + "size":51539607552, + "align":16777216, + "available_size":50465865728, + "max_available_extent":50465865728, + "type":"pmem", + "iset_id":4694373484956518536, + "persistence_domain":"memory_controller" + }, + { + "dev":"region0", + "size":51539607552, + "align":16777216, + "available_size":51539607552, + "max_available_extent":51539607552, + "type":"pmem", + "iset_id":4588538894081362056, + "persistence_domain":"memory_controller" + } +]""" + +# ndctl_region_empty: the mock return value of pmem_run_command with: +# ndctl list -R +ndctl_region_empty = "" + +# ndctl_without_size: the mock return value of pmem_run_command with: +# ndctl create-namespace -t pmem -m sector +ndctl_create_without_size = """{ + "dev":"namespace1.0", + "mode":"sector", + "size":"47.95 GiB (51.49 GB)", + "uuid":"1aca23a5-941c-4f4a-9d88-e531f0b5a27e", + "sector_size":4096, + "blockdev":"pmem1s" +}""" + +# ndctl_list_N: the mock return value of pmem_run_command with: +# ndctl list -N +ndctl_list_N = """[ + { + "dev":"namespace1.0", + "mode":"sector", + "size":51488243712, + "uuid":"1aca23a5-941c-4f4a-9d88-e531f0b5a27e", + "sector_size":4096, + "blockdev":"pmem1s" + } +]""" + +# ndctl_result_1G: the mock return value of pmem_run_command with: +# ndctl create-namespace -t pmem -m sector -s 1073741824 +ndctl_create_1G = """{ + "dev":"namespace0.0", + "mode":"sector", + "size":"1021.97 MiB (1071.62 MB)", + "uuid":"5ba4e51b-3028-4b06-8495-b6834867a9af", + "sector_size":4096, + "blockdev":"pmem0s" +}""" + +# ndctl_result_640M: the mock return value of pmem_run_command with: +# ndctl create-namespace -t pmem -m raw -s 671088640 +ndctl_create_640M = """{ + "dev":"namespace1.0", + "mode":"raw", + "size":"640.00 MiB (671.09 MB)", + "uuid":"5ac1f81d-86e6-4f07-9460-8c4d37027f7a", + "sector_size":512, + "blockdev":"pmem1" +}""" + +# ndctl_list_N_tow_namespaces: the mock return value of pmem_run_command with: +# ndctl list -N +ndctl_list_N_two_namespaces = """[ + { + "dev":"namespace1.0", + "mode":"sector", + "size":1071616000, + "uuid":"afcf050d-3a8b-4f48-88a5-16d7c40ab2d8", + "sector_size":4096, + "blockdev":"pmem1s" + }, + { + "dev":"namespace1.1", + "mode":"raw", + "size":671088640, + "uuid":"fb704339-729b-4cc7-b260-079f2633d84f", + "sector_size":512, + "blockdev":"pmem1.1" + } +]""" + + +class TestPmem(ModuleTestCase): + def setUp(self): + super(TestPmem, self).setUp() + self.module = pmem_module + + self.mock_run_command = (patch('ansible.module_utils.basic.AnsibleModule.run_command')) + self.mock_get_bin_path = (patch('ansible.module_utils.basic.AnsibleModule.get_bin_path')) + + self.run_command = self.mock_run_command.start() + self.get_bin_path = self.mock_get_bin_path.start() + + self.mock_pmem_is_dcpmm_installed = (patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_is_dcpmm_installed', return_value="")) + self.mock_pmem_init_env = (patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_init_env', return_value="")) + + self.pmem_is_dcpmm_installed = self.mock_pmem_is_dcpmm_installed.start() + self.pmem_init_env = self.mock_pmem_init_env.start() + + def tearDown(self): + super(TestPmem, self).tearDown() + self.mock_get_bin_path.stop() + self.mock_run_command.stop() + self.mock_pmem_is_dcpmm_installed.stop() + self.mock_pmem_init_env.stop() + + def result_check(self, result, socket, appdirect, memmode, reserved): + self.assertTrue(result.exception.args[0]['changed']) + self.assertTrue(result.exception.args[0]['reboot_required']) + + test_result = result.exception.args[0]['result'] + + if socket: + maxIndex = 1 + else: + maxIndex = 0 + + for i in range(0, maxIndex): + self.assertAlmostEqual(test_result[i]['appdirect'], appdirect[i]) + self.assertAlmostEqual(test_result[i]['memorymode'], memmode[i]) + self.assertAlmostEqual(test_result[i]['reserved'], reserved[i]) + if socket: + self.assertAlmostEqual(test_result[i]['socket'], i) + + def result_check_ns(self, result, namespace): + self.assertTrue(result.exception.args[0]['changed']) + self.assertFalse(result.exception.args[0]['reboot_required']) + + test_result = result.exception.args[0]['result'] + expected = json.loads(namespace) + + for i, result in enumerate(test_result): + self.assertEqual(result['dev'], expected[i]['dev']) + self.assertEqual(result['size'], expected[i]['size']) + + def test_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + pmem_module.main() + + def test_fail_when_appdirect_only(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'appdirect': 10, + }) + pmem_module.main() + + def test_fail_when_MemosyMode_only(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'memorymode': 70, + }) + pmem_module.main() + + def test_fail_when_reserved_only(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'reserved': 10, + }) + pmem_module.main() + + def test_fail_when_appdirect_memorymode_reserved_total_not_100(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'appdirect': 10, + 'memorymode': 70, + 'reserved': 10, + }) + pmem_module.main() + + def test_when_appdirect_memorymode(self): + set_module_args({ + 'appdirect': 10, + 'memorymode': 70, + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[goal_plain, goal, dimmlist]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check(result, False, [25769803776], [188978561024], [328230764544]) + + def test_when_appdirect_memorymode_reserved(self): + set_module_args({ + 'appdirect': 10, + 'memorymode': 70, + 'reserved': 20, + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[goal_plain, goal, dimmlist]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check(result, False, [25769803776], [188978561024], [328230764544]) + + def test_when_appdirect_notinterleaved_memorymode_reserved(self): + set_module_args({ + 'appdirect': 10, + 'appdirect_interleaved': False, + 'memorymode': 70, + 'reserved': 20, + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[goal_plain, goal, dimmlist]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check(result, False, [25769803776], [188978561024], [328230764544]) + + def test_fail_when_socket_id_appdirect(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'socket': [ + { + 'id': 0, + 'appdirect': 10, + }, + { + 'id': 1, + 'appdirect': 10, + }, + ], + }) + pmem_module.main() + + def test_fail_when_socket0_id_memorymode_socket1_id_appdirect(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'socket': [ + { + 'id': 0, + ' memorymode': 70, + }, + { + 'id': 1, + 'appdirect': 10, + }, + ], + }) + pmem_module.main() + + def test_fail_when_socket0_without_id(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'socket': [ + { + 'appdirect': 10, + 'memorymode': 70, + }, + { + 'id': 1, + 'appdirect': 10, + 'memorymode': 70, + }, + ], + }) + pmem_module.main() + + def test_when_socket0_and_1_appdirect_memorymode(self): + set_module_args({ + 'socket': [ + { + 'id': 0, + 'appdirect': 10, + 'memorymode': 70, + }, + { + 'id': 1, + 'appdirect': 10, + 'memorymode': 70, + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ + show_skt, goal_plain_sk0, goal_sk0, dimmlist_sk0, goal_plain_sk1, goal_sk1, dimmlist_sk1]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check( + result, True, [12884901888, 12884901888], [94489280512, 94489280512], [164115382272, 164115382272]) + + def test_when_socket0_and_1_appdirect_memorymode_reserved(self): + set_module_args({ + 'socket': [ + { + 'id': 0, + 'appdirect': 10, + 'memorymode': 70, + 'reserved': 20, + }, + { + 'id': 1, + 'appdirect': 10, + 'memorymode': 70, + 'reserved': 20, + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ + show_skt, goal_plain_sk0, goal_sk0, dimmlist_sk0, goal_plain_sk1, goal_sk1, dimmlist_sk1]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check( + result, True, [12884901888, 12884901888], [94489280512, 94489280512], [164115382272, 164115382272]) + + def test_when_socket0_appdirect_notinterleaved_memorymode_reserved_socket1_appdirect_memorymode_reserved(self): + set_module_args({ + 'socket': [ + { + 'id': 0, + 'appdirect': 10, + 'appdirect_interleaved': False, + 'memorymode': 70, + 'reserved': 20, + }, + { + 'id': 1, + 'appdirect': 10, + 'memorymode': 70, + 'reserved': 20, + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ + show_skt, goal_plain_sk0, goal_sk0, dimmlist_sk0, goal_plain_sk1, goal_sk1, dimmlist_sk1]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check( + result, True, [12884901888, 12884901888], [94489280512, 94489280512], [164115382272, 164115382272]) + + def test_fail_when_namespace_without_mode(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'size': '1GB', + 'type': 'pmem', + }, + { + 'size': '2GB', + 'type': 'blk', + }, + ], + }) + pmem_module.main() + + def test_fail_when_region_is_empty(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'size': '1GB', + 'type': 'pmem', + 'mode': 'sector', + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region_empty]): + pmem_module.main() + + def test_fail_when_namespace_invalid_size(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'size': '1XXX', + 'type': 'pmem', + 'mode': 'sector', + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region]): + pmem_module.main() + + def test_fail_when_size_is_invalid_alignment(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'size': '400MB', + 'type': 'pmem', + 'mode': 'sector' + }, + { + 'size': '500MB', + 'type': 'pmem', + 'mode': 'sector' + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region]): + pmem_module.main() + + def test_fail_when_blk_is_unsupported_type(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'size': '4GB', + 'type': 'pmem', + 'mode': 'sector' + }, + { + 'size': '5GB', + 'type': 'blk', + 'mode': 'sector' + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region]): + pmem_module.main() + + def test_fail_when_size_isnot_set_to_multiple_namespaces(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'type': 'pmem', + 'mode': 'sector' + }, + { + 'size': '500GB', + 'type': 'blk', + 'mode': 'sector' + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region]): + pmem_module.main() + + def test_fail_when_size_of_namespace_over_available(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'namespace': [ + { + 'size': '400GB', + 'type': 'pmem', + 'mode': 'sector' + }, + { + 'size': '500GB', + 'type': 'pmem', + 'mode': 'sector' + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region]): + pmem_module.main() + + def test_when_namespace0_without_size(self): + set_module_args({ + 'namespace': [ + { + 'type': 'pmem', + 'mode': 'sector' + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region, ndctl_create_without_size, ndctl_list_N]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check_ns(result, ndctl_list_N) + + def test_when_namespace0_with_namespace_append(self): + set_module_args({ + 'namespace': [ + { + 'size': '640MB', + 'type': 'pmem', + 'mode': 'raw' + }, + ], + 'namespace_append': True, + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region, ndctl_create_640M, ndctl_list_N_two_namespaces]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check_ns(result, ndctl_list_N_two_namespaces) + + def test_when_namespace0_1GiB_pmem_sector_namespace1_640MiB_pmem_raw(self): + set_module_args({ + 'namespace': [ + { + 'size': '1GB', + 'type': 'pmem', + 'mode': 'sector' + }, + { + 'size': '640MB', + 'type': 'pmem', + 'mode': 'raw', + }, + ], + }) + with patch( + 'ansible_collections.community.general.plugins.modules.pmem.PersistentMemory.pmem_run_command', + side_effect=[ndctl_region, ndctl_create_1G, ndctl_create_640M, ndctl_list_N_two_namespaces]): + with self.assertRaises(AnsibleExitJson) as result: + pmem_module.main() + self.result_check_ns(result, ndctl_list_N_two_namespaces) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org.py new file mode 100644 index 000000000..94809784b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021 Florian Dambrine +# 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 + +import sys + +from ansible.module_utils.common.dict_transformations import dict_merge +from ansible.module_utils.six import iteritems +from ansible_collections.community.general.plugins.modules import ( + pritunl_org, +) +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.plugins.module_utils.net_tools.pritunl.test_api import ( + PritunlDeleteOrganizationMock, + PritunlListOrganizationMock, + PritunlListOrganizationAfterPostMock, + PritunlPostOrganizationMock, +) +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +__metaclass__ = type + + +class TestPritunlOrg(ModuleTestCase): + def setUp(self): + super(TestPritunlOrg, self).setUp() + self.module = pritunl_org + + # Add backward compatibility + if sys.version_info < (3, 2): + self.assertRegex = self.assertRegexpMatches + + def tearDown(self): + super(TestPritunlOrg, self).tearDown() + + def patch_add_pritunl_organization(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._post_pritunl_organization", + autospec=True, + **kwds + ) + + def patch_delete_pritunl_organization(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._delete_pritunl_organization", + autospec=True, + **kwds + ) + + def patch_get_pritunl_organizations(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._get_pritunl_organizations", + autospec=True, + **kwds + ) + + def test_without_parameters(self): + """Test without parameters""" + set_module_args({}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_present(self): + """Test Pritunl organization creation.""" + org_params = {"name": "NewOrg"} + set_module_args( + dict_merge( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + }, + org_params, + ) + ) + # Test creation + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as mock_get: + with self.patch_add_pritunl_organization( + side_effect=PritunlPostOrganizationMock + ) as mock_add: + with self.assertRaises(AnsibleExitJson) as create_result: + self.module.main() + + create_exc = create_result.exception.args[0] + + self.assertTrue(create_exc["changed"]) + self.assertEqual(create_exc["response"]["name"], org_params["name"]) + self.assertEqual(create_exc["response"]["user_count"], 0) + + # Test module idempotency + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationAfterPostMock + ) as mock_get: + with self.patch_add_pritunl_organization( + side_effect=PritunlPostOrganizationMock + ) as mock_add: + with self.assertRaises(AnsibleExitJson) as idempotent_result: + self.module.main() + + idempotent_exc = idempotent_result.exception.args[0] + + # Ensure both calls resulted in the same returned value + # except for changed which should be false the second time + for k, v in iteritems(idempotent_exc): + if k == "changed": + self.assertFalse(idempotent_exc[k]) + else: + self.assertEqual(create_exc[k], idempotent_exc[k]) + + def test_absent(self): + """Test organization removal from Pritunl.""" + org_params = {"name": "NewOrg"} + set_module_args( + dict_merge( + { + "state": "absent", + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + }, + org_params, + ) + ) + # Test deletion + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationAfterPostMock + ) as mock_get: + with self.patch_delete_pritunl_organization( + side_effect=PritunlDeleteOrganizationMock + ) as mock_delete: + with self.assertRaises(AnsibleExitJson) as delete_result: + self.module.main() + + delete_exc = delete_result.exception.args[0] + + self.assertTrue(delete_exc["changed"]) + self.assertEqual(delete_exc["response"], {}) + + # Test module idempotency + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as mock_get: + with self.patch_delete_pritunl_organization( + side_effect=PritunlDeleteOrganizationMock + ) as mock_add: + with self.assertRaises(AnsibleExitJson) as idempotent_result: + self.module.main() + + idempotent_exc = idempotent_result.exception.args[0] + + # Ensure both calls resulted in the same returned value + # except for changed which should be false the second time + self.assertFalse(idempotent_exc["changed"]) + self.assertEqual(idempotent_exc["response"], delete_exc["response"]) + + def test_absent_with_existing_users(self): + """Test organization removal with attached users should fail except if force is true.""" + module_args = { + "state": "absent", + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "name": "GumGum", + } + set_module_args(module_args) + + # Test deletion + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as mock_get: + with self.patch_delete_pritunl_organization( + side_effect=PritunlDeleteOrganizationMock + ) as mock_delete: + with self.assertRaises(AnsibleFailJson) as failure_result: + self.module.main() + + failure_exc = failure_result.exception.args[0] + + self.assertRegex(failure_exc["msg"], "Can not remove organization") + + # Switch force=True which should run successfully + set_module_args(dict_merge(module_args, {"force": True})) + + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as mock_get: + with self.patch_delete_pritunl_organization( + side_effect=PritunlDeleteOrganizationMock + ) as mock_delete: + with self.assertRaises(AnsibleExitJson) as delete_result: + self.module.main() + + delete_exc = delete_result.exception.args[0] + + self.assertTrue(delete_exc["changed"]) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org_info.py new file mode 100644 index 000000000..dc33c3d8c --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_org_info.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Florian Dambrine +# 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 + +import sys + +from ansible_collections.community.general.plugins.modules import ( + pritunl_org_info, +) +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.plugins.module_utils.net_tools.pritunl.test_api import ( + PritunlListOrganizationMock, + PritunlEmptyOrganizationMock, +) +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +__metaclass__ = type + + +class TestPritunlOrgInfo(ModuleTestCase): + def setUp(self): + super(TestPritunlOrgInfo, self).setUp() + self.module = pritunl_org_info + + # Add backward compatibility + if sys.version_info < (3, 2): + self.assertRegex = self.assertRegexpMatches + + def tearDown(self): + super(TestPritunlOrgInfo, self).tearDown() + + def patch_get_pritunl_organizations(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._get_pritunl_organizations", + autospec=True, + **kwds + ) + + def test_without_parameters(self): + """Test without parameters""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + set_module_args({}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + self.assertEqual(org_mock.call_count, 0) + + def test_list_empty_organizations(self): + """Listing all organizations even when no org exists should be valid.""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlEmptyOrganizationMock + ) as org_mock: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + + exc = result.exception.args[0] + self.assertEqual(len(exc["organizations"]), 0) + + def test_list_specific_organization(self): + """Listing a specific organization should be valid.""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "org": "GumGum", + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + + exc = result.exception.args[0] + self.assertEqual(len(exc["organizations"]), 1) + + def test_list_unknown_organization(self): + """Listing an unknown organization should result in a failure.""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.assertRaises(AnsibleFailJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "org": "Unknown", + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + + exc = result.exception.args[0] + self.assertRegex(exc["msg"], "does not exist") + + def test_list_all_organizations(self): + """Listing all organizations should be valid.""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + + exc = result.exception.args[0] + self.assertEqual(len(exc["organizations"]), 3) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user.py new file mode 100644 index 000000000..112083918 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021 Florian Dambrine +# 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 + +import sys + +from ansible.module_utils.common.dict_transformations import dict_merge +from ansible.module_utils.six import iteritems +from ansible_collections.community.general.plugins.modules import ( + pritunl_user, +) +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.plugins.module_utils.net_tools.pritunl.test_api import ( + PritunlDeleteUserMock, + PritunlListOrganizationMock, + PritunlListUserMock, + PritunlPostUserMock, + PritunlPutUserMock, +) +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +__metaclass__ = type + + +def mock_pritunl_api(func, **kwargs): + def wrapped(self=None): + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ): + with self.patch_get_pritunl_users(side_effect=PritunlListUserMock): + with self.patch_add_pritunl_users(side_effect=PritunlPostUserMock): + with self.patch_delete_pritunl_users( + side_effect=PritunlDeleteUserMock + ): + func(self, **kwargs) + + return wrapped + + +class TestPritunlUser(ModuleTestCase): + def setUp(self): + super(TestPritunlUser, self).setUp() + self.module = pritunl_user + + # Add backward compatibility + if sys.version_info < (3, 2): + self.assertRegex = self.assertRegexpMatches + + def tearDown(self): + super(TestPritunlUser, self).tearDown() + + def patch_get_pritunl_users(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._get_pritunl_users", + autospec=True, + **kwds + ) + + def patch_add_pritunl_users(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._post_pritunl_user", + autospec=True, + **kwds + ) + + def patch_update_pritunl_users(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._put_pritunl_user", + autospec=True, + **kwds + ) + + def patch_delete_pritunl_users(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._delete_pritunl_user", + autospec=True, + **kwds + ) + + def patch_get_pritunl_organizations(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._get_pritunl_organizations", + autospec=True, + **kwds + ) + + def test_without_parameters(self): + """Test without parameters""" + set_module_args({}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + @mock_pritunl_api + def test_present(self): + """Test Pritunl user creation and update.""" + user_params = { + "user_name": "alice", + "user_email": "alice@company.com", + } + set_module_args( + dict_merge( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "GumGum", + }, + user_params, + ) + ) + + with self.patch_update_pritunl_users( + side_effect=PritunlPostUserMock + ) as post_mock: + with self.assertRaises(AnsibleExitJson) as create_result: + self.module.main() + + create_exc = create_result.exception.args[0] + + self.assertTrue(create_exc["changed"]) + self.assertEqual(create_exc["response"]["name"], user_params["user_name"]) + self.assertEqual(create_exc["response"]["email"], user_params["user_email"]) + self.assertFalse(create_exc["response"]["disabled"]) + + # Changing user from alice to bob should update certain fields only + + new_user_params = { + "user_name": "bob", + "user_email": "bob@company.com", + "user_disabled": True, + } + set_module_args( + dict_merge( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "GumGum", + }, + new_user_params, + ) + ) + + with self.patch_update_pritunl_users( + side_effect=PritunlPutUserMock + ) as put_mock: + + with self.assertRaises(AnsibleExitJson) as update_result: + self.module.main() + + update_exc = update_result.exception.args[0] + + # Ensure only certain settings changed and the rest remained untouched. + for k, v in iteritems(update_exc): + if k in new_user_params: + assert update_exc[k] == v + else: + assert update_exc[k] == create_exc[k] + + @mock_pritunl_api + def test_absent(self): + """Test user removal from Pritunl.""" + set_module_args( + { + "state": "absent", + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "GumGum", + "user_name": "florian", + } + ) + + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + + exc = result.exception.args[0] + + self.assertTrue(exc["changed"]) + self.assertEqual(exc["response"], {}) + + @mock_pritunl_api + def test_absent_failure(self): + """Test user removal from a non existing organization.""" + set_module_args( + { + "state": "absent", + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "Unknown", + "user_name": "floria@company.com", + } + ) + + with self.assertRaises(AnsibleFailJson) as result: + self.module.main() + + exc = result.exception.args[0] + + self.assertRegex(exc["msg"], "Can not remove user") diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user_info.py new file mode 100644 index 000000000..5aae15d96 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pritunl_user_info.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Florian Dambrine +# 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 + +import sys + +from ansible_collections.community.general.plugins.modules import ( + pritunl_user_info, +) +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.plugins.module_utils.net_tools.pritunl.test_api import ( + PritunlListOrganizationMock, + PritunlListUserMock, +) +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, + AnsibleFailJson, + ModuleTestCase, + set_module_args, +) + +__metaclass__ = type + + +class TestPritunlUserInfo(ModuleTestCase): + def setUp(self): + super(TestPritunlUserInfo, self).setUp() + self.module = pritunl_user_info + + # Add backward compatibility + if sys.version_info < (3, 2): + self.assertRegex = self.assertRegexpMatches + + def tearDown(self): + super(TestPritunlUserInfo, self).tearDown() + + def patch_get_pritunl_users(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._get_pritunl_users", + autospec=True, + **kwds + ) + + def patch_get_pritunl_organizations(self, **kwds): + return patch( + "ansible_collections.community.general.plugins.module_utils.net_tools.pritunl.api._get_pritunl_organizations", + autospec=True, + **kwds + ) + + def test_without_parameters(self): + """Test without parameters""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.patch_get_pritunl_users( + side_effect=PritunlListUserMock + ) as user_mock: + set_module_args({}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + self.assertEqual(org_mock.call_count, 0) + self.assertEqual(user_mock.call_count, 0) + + def test_missing_organization(self): + """Failure must occur when the requested organization is not found.""" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.patch_get_pritunl_users( + side_effect=PritunlListUserMock + ) as user_mock: + with self.assertRaises(AnsibleFailJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "Unknown", + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + self.assertEqual(user_mock.call_count, 0) + + exc = result.exception.args[0] + self.assertRegex(exc["msg"], "Can not list users from the organization") + + def test_get_all_client_users_from_organization(self): + """ + The list of all Pritunl client users from the organization must be returned when no user specified. + """ + expected_user_type = "client" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.patch_get_pritunl_users( + side_effect=PritunlListUserMock + ) as user_mock: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "GumGum", + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + self.assertEqual(user_mock.call_count, 1) + + exc = result.exception.args[0] + # module should not report changes + self.assertFalse(exc["changed"]) + # user_type when not provided is set client and should only return client user type + self.assertEqual(len(exc["users"]), 1) + for user in exc["users"]: + self.assertEqual(user["type"], expected_user_type) + + def test_get_specific_server_user_from_organization(self): + """ + Retrieving a specific user from the organization must return a single record. + """ + expected_user_type = "server" + expected_user_name = "ops" + with self.patch_get_pritunl_organizations( + side_effect=PritunlListOrganizationMock + ) as org_mock: + with self.patch_get_pritunl_users( + side_effect=PritunlListUserMock + ) as user_mock: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args( + { + "pritunl_api_token": "token", + "pritunl_api_secret": "secret", + "pritunl_url": "https://pritunl.domain.com", + "organization": "GumGum", + "user_name": expected_user_name, + "user_type": expected_user_type, + } + ) + self.module.main() + + self.assertEqual(org_mock.call_count, 1) + self.assertEqual(user_mock.call_count, 1) + + exc = result.exception.args[0] + # module should not report changes + self.assertFalse(exc["changed"]) + self.assertEqual(len(exc["users"]), 1) + for user in exc["users"]: + self.assertEqual(user["type"], expected_user_type) + self.assertEqual(user["name"], expected_user_name) 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 new file mode 100644 index 000000000..531185102 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py @@ -0,0 +1,20 @@ +# -*- 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 ansible_collections.community.general.plugins.modules.proxmox_kvm import parse_dev, parse_mac + + +def test_parse_mac(): + assert 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' 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 new file mode 100644 index 000000000..4bdcaa8b7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, 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 +import pytest + +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 +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + + +def get_resources(type): + return [{"diskwrite": 0, + "vmid": 100, + "node": "localhost", + "id": "lxc/100", + "maxdisk": 10000, + "template": 0, + "disk": 10000, + "uptime": 10000, + "maxmem": 10000, + "maxcpu": 1, + "netin": 10000, + "type": "lxc", + "netout": 10000, + "mem": 10000, + "diskread": 10000, + "cpu": 0.01, + "name": "test-lxc", + "status": "running"}] + + +def fake_api(mocker): + r = mocker.MagicMock() + r.cluster.resources.get = MagicMock(side_effect=get_resources) + return r + + +def test_proxmox_snap_without_argument(capfd): + set_module_args({}) + with pytest.raises(SystemExit) as results: + proxmox_snap.main() + + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_create_snapshot_check_mode(connect_mock, capfd, mocker): + set_module_args({"hostname": "test-lxc", + "api_user": "root@pam", + "api_password": "secret", + "api_host": "127.0.0.1", + "state": "present", + "snapname": "test", + "timeout": "1", + "force": True, + "_ansible_check_mode": True}) + proxmox_utils.HAS_PROXMOXER = True + connect_mock.side_effect = lambda: fake_api(mocker) + with pytest.raises(SystemExit) as results: + proxmox_snap.main() + + out, err = capfd.readouterr() + assert not err + assert not json.loads(out)['changed'] + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_remove_snapshot_check_mode(connect_mock, capfd, mocker): + set_module_args({"hostname": "test-lxc", + "api_user": "root@pam", + "api_password": "secret", + "api_host": "127.0.0.1", + "state": "absent", + "snapname": "test", + "timeout": "1", + "force": True, + "_ansible_check_mode": True}) + proxmox_utils.HAS_PROXMOXER = True + connect_mock.side_effect = lambda: fake_api(mocker) + with pytest.raises(SystemExit) as results: + proxmox_snap.main() + + out, err = capfd.readouterr() + assert not err + assert not json.loads(out)['changed'] + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_rollback_snapshot_check_mode(connect_mock, capfd, mocker): + set_module_args({"hostname": "test-lxc", + "api_user": "root@pam", + "api_password": "secret", + "api_host": "127.0.0.1", + "state": "rollback", + "snapname": "test", + "timeout": "1", + "force": True, + "_ansible_check_mode": True}) + proxmox_utils.HAS_PROXMOXER = True + connect_mock.side_effect = lambda: fake_api(mocker) + with pytest.raises(SystemExit) as results: + proxmox_snap.main() + + out, err = capfd.readouterr() + assert not err + output = json.loads(out) + assert not output['changed'] + assert output['msg'] == "Snapshot test does not exist" 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 new file mode 100644 index 000000000..0d1b5a7bf --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Andreas Botzner (@paginabianca) +# 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 +# +# Proxmox Tasks module unit tests. +# The API responses used in these tests were recorded from PVE version 6.4-8 + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest +import json + +from ansible_collections.community.general.plugins.modules import proxmox_tasks_info +import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + +NODE = 'node01' +TASK_UPID = 'UPID:iaclab-01-01:000029DD:1599528B:6108F068:srvreload:networking:root@pam:' +TASKS = [ + { + "endtime": 1629092710, + "id": "networking", + "node": "iaclab-01-01", + "pid": 3539, + "pstart": 474062216, + "starttime": 1629092709, + "status": "OK", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:00000DD3:1C419D88:6119FB65:srvreload:networking:root@pam:", + "user": "root@pam" + }, + { + "endtime": 1627975785, + "id": "networking", + "node": "iaclab-01-01", + "pid": 10717, + "pstart": 362369675, + "starttime": 1627975784, + "status": "command 'ifreload -a' failed: exit code 1", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:000029DD:1599528B:6108F068:srvreload:networking:root@pam:", + "user": "root@pam" + }, + { + "endtime": 1627975503, + "id": "networking", + "node": "iaclab-01-01", + "pid": 6778, + "pstart": 362341540, + "starttime": 1627975503, + "status": "OK", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:00001A7A:1598E4A4:6108EF4F:srvreload:networking:root@pam:", + "user": "root@pam" + } +] +EXPECTED_TASKS = [ + { + "endtime": 1629092710, + "id": "networking", + "node": "iaclab-01-01", + "pid": 3539, + "pstart": 474062216, + "starttime": 1629092709, + "status": "OK", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:00000DD3:1C419D88:6119FB65:srvreload:networking:root@pam:", + "user": "root@pam", + "failed": False + }, + { + "endtime": 1627975785, + "id": "networking", + "node": "iaclab-01-01", + "pid": 10717, + "pstart": 362369675, + "starttime": 1627975784, + "status": "command 'ifreload -a' failed: exit code 1", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:000029DD:1599528B:6108F068:srvreload:networking:root@pam:", + "user": "root@pam", + "failed": True + }, + { + "endtime": 1627975503, + "id": "networking", + "node": "iaclab-01-01", + "pid": 6778, + "pstart": 362341540, + "starttime": 1627975503, + "status": "OK", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:00001A7A:1598E4A4:6108EF4F:srvreload:networking:root@pam:", + "user": "root@pam", + "failed": False + } +] + +EXPECTED_SINGLE_TASK = [ + { + "endtime": 1627975785, + "id": "networking", + "node": "iaclab-01-01", + "pid": 10717, + "pstart": 362369675, + "starttime": 1627975784, + "status": "command 'ifreload -a' failed: exit code 1", + "type": "srvreload", + "upid": "UPID:iaclab-01-01:000029DD:1599528B:6108F068:srvreload:networking:root@pam:", + "user": "root@pam", + "failed": True + }, +] + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_without_required_parameters(connect_mock, capfd, mocker): + set_module_args({}) + with pytest.raises(SystemExit): + proxmox_tasks_info.main() + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + + +def mock_api_tasks_response(mocker): + m = mocker.MagicMock() + g = mocker.MagicMock() + m.nodes = mocker.MagicMock(return_value=g) + g.tasks.get = mocker.MagicMock(return_value=TASKS) + return m + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_get_tasks(connect_mock, capfd, mocker): + set_module_args({'api_host': 'proxmoxhost', + 'api_user': 'root@pam', + 'api_password': 'supersecret', + 'node': NODE}) + + connect_mock.side_effect = lambda: mock_api_tasks_response(mocker) + proxmox_utils.HAS_PROXMOXER = True + + with pytest.raises(SystemExit): + proxmox_tasks_info.main() + out, err = capfd.readouterr() + assert not err + assert len(json.loads(out)['proxmox_tasks']) != 0 + assert not json.loads(out)['changed'] + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_get_single_task(connect_mock, capfd, mocker): + set_module_args({'api_host': 'proxmoxhost', + 'api_user': 'root@pam', + 'api_password': 'supersecret', + 'node': NODE, + 'task': TASK_UPID}) + + connect_mock.side_effect = lambda: mock_api_tasks_response(mocker) + proxmox_utils.HAS_PROXMOXER = True + + with pytest.raises(SystemExit): + proxmox_tasks_info.main() + out, err = capfd.readouterr() + assert not err + assert len(json.loads(out)['proxmox_tasks']) == 1 + assert json.loads(out) + assert not json.loads(out)['changed'] + + +@patch('ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect') +def test_get_non_existent_task(connect_mock, capfd, mocker): + set_module_args({'api_host': 'proxmoxhost', + 'api_user': 'root@pam', + 'api_password': 'supersecret', + 'node': NODE, + 'task': 'UPID:nonexistent'}) + + connect_mock.side_effect = lambda: mock_api_tasks_response(mocker) + proxmox_utils.HAS_PROXMOXER = True + + with pytest.raises(SystemExit): + proxmox_tasks_info.main() + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + assert 'proxmox_tasks' not in json.loads(out) + assert not json.loads(out)['changed'] + assert json.loads( + out)['msg'] == 'Task: UPID:nonexistent does not exist on node: node01.' 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 new file mode 100644 index 000000000..f62523e7f --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py @@ -0,0 +1,227 @@ +# -*- coding: utf-8 -*- +# Author: Alexei Znamensky (russoz@gmail.com) +# Largely adapted from test_redhat_subscription by +# Jiri Hnidek (jhnidek@redhat.com) +# +# Copyright (c) Alexei Znamensky (russoz@gmail.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 json + +from collections import namedtuple +from ansible_collections.community.general.plugins.modules import puppet + +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"] 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 new file mode 100644 index 000000000..4bf272916 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py @@ -0,0 +1,1337 @@ +# -*- coding: utf-8 -*- +# 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 json + +from ansible.module_utils import basic +from ansible_collections.community.general.plugins.modules import redhat_subscription + +import pytest + +TESTED_MODULE = redhat_subscription.__name__ + + +@pytest.fixture +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.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.getuid', + return_value=0) + + +@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_without_required_parameters_unregistered(mocker, capfd, patch_redhat_subscription): + """ + Failure must occurs when all parameters are missing + """ + mock_run_command = mocker.patch.object( + basic.AnsibleModule, + 'run_command', + return_value=(1, 'This system is not yet registered.', '')) + + with pytest.raises(SystemExit): + redhat_subscription.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'state is present but any of the following are missing' in results['msg'] + + +@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_without_required_parameters_registered(mocker, capfd, patch_redhat_subscription): + """ + System already registered, no parameters required (state=present is the + default) + """ + mock_run_command = mocker.patch.object( + basic.AnsibleModule, + 'run_command', + return_value=(0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '')) + + with pytest.raises(SystemExit): + redhat_subscription.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert 'changed' in results + if 'msg' in results: + assert results['msg'] == 'System already registered.' + + +TEST_CASES = [ + # Test the case, when the system is already registered + [ + { + 'state': 'present', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin' + }, + { + 'id': 'test_already_registered_system', + 'run_command.calls': [ + ( + # Calling of following command will be asserted + ['/testbin/subscription-manager', 'identity'], + # Was return code checked? + {'check_rc': False}, + # Mock of returned code, stdout and stderr + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ) + ], + 'changed': False, + 'msg': 'System already registered.' + } + ], + # Already registered system without credentials specified + [ + { + 'state': 'present', + }, + { + 'id': 'test_already_registered_system', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ) + ], + 'changed': False, + 'msg': 'System already registered.' + } + ], + # Test simple registration using username and password + [ + { + 'state': 'present', + 'server_hostname': 'satellite.company.com', + 'username': 'admin', + 'password': 'admin', + }, + { + 'id': 'test_registeration_username_password', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, '', '') + ), + ( + ['/testbin/subscription-manager', 'config', '--server.hostname=satellite.company.com'], + {'check_rc': True}, + (0, '', '') + ), + ( + ['/testbin/subscription-manager', 'register', + '--username', 'admin', + '--password', 'admin'], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'satellite.company.com'." + } + ], + # Test simple registration using token + [ + { + 'state': 'present', + 'server_hostname': 'satellite.company.com', + 'token': 'fake_token', + }, + { + 'id': 'test_registeration_token', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, '', '') + ), + ( + ['/testbin/subscription-manager', 'config', '--server.hostname=satellite.company.com'], + {'check_rc': True}, + (0, '', '') + ), + ( + ['/testbin/subscription-manager', 'register', + '--token', 'fake_token'], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'satellite.company.com'." + } + ], + # Test unregistration, when system is unregistered + [ + { + 'state': 'absent', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + }, + { + 'id': 'test_unregisteration', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ), + ( + ['/testbin/subscription-manager', 'remove', '--all'], + {'check_rc': True}, + (0, '', '') + ), + ( + ['/testbin/subscription-manager', 'unregister'], + {'check_rc': True}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully unregistered from subscription.rhsm.redhat.com." + } + ], + # Test unregistration of already unregistered system + [ + { + 'state': 'absent', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + }, + { + 'id': 'test_unregisteration_of_unregistered_system', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ) + ], + 'changed': False, + 'msg': "System already unregistered." + } + ], + # Test registration using activation key + [ + { + 'state': 'present', + 'server_hostname': 'satellite.company.com', + 'activationkey': 'some-activation-key', + 'org_id': 'admin' + }, + { + 'id': 'test_registeration_activation_key', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + ['/testbin/subscription-manager', 'config', '--server.hostname=satellite.company.com'], + {'check_rc': True}, + (0, '', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--activationkey', 'some-activation-key' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'satellite.company.com'." + } + ], + # Test of registration using username and password with auto-attach option + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'auto_attach': 'true' + }, + { + 'id': 'test_registeration_username_password_auto_attach', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--auto-attach', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of force registration despite the system is already registered + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'force_register': 'true' + }, + { + 'id': 'test_force_registeration_username_password', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'This system already registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--force', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration with arguments that are not part of register options but needs to be configured + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'force_register': 'true', + 'server_prefix': '/rhsm', + 'server_port': '443' + }, + { + 'id': 'test_arguments_not_in_register_options', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'This system already registered.', '') + ), + ( + ['/testbin/subscription-manager', 'config', + '--server.port=443', + '--server.prefix=/rhsm' + ], + {'check_rc': True}, + (0, '', '') + ), + ( + ['/testbin/subscription-manager', 'register', + '--force', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin'], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration using username, password and proxy options + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'force_register': 'true', + 'server_proxy_hostname': 'proxy.company.com', + 'server_proxy_scheme': 'https', + 'server_proxy_port': '12345', + 'server_proxy_user': 'proxy_user', + 'server_proxy_password': 'secret_proxy_password' + }, + { + 'id': 'test_registeration_username_password_proxy_options', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'This system already registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'config', + '--server.proxy_hostname=proxy.company.com', + '--server.proxy_password=secret_proxy_password', + '--server.proxy_port=12345', + '--server.proxy_scheme=https', + '--server.proxy_user=proxy_user' + ], + {'check_rc': True}, + (0, '', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--force', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration using username and password and attach to pool + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'pool': 'ff8080816b8e967f016b8e99632804a6' + }, + { + 'id': 'test_registeration_username_password_pool', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ), + ( + [ + 'subscription-manager list --available', + {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}}, + (0, + ''' ++-------------------------------------------+ + Available Subscriptions ++-------------------------------------------+ +Subscription Name: SP Server Premium (S: Premium, U: Production, R: SP Server) +Provides: SP Server Bits +SKU: sp-server-prem-prod +Contract: 0 +Pool ID: ff8080816b8e967f016b8e99632804a6 +Provides Management: Yes +Available: 5 +Suggested: 1 +Service Type: L1-L3 +Roles: SP Server +Service Level: Premium +Usage: Production +Add-ons: +Subscription Type: Standard +Starts: 06/25/19 +Ends: 06/24/20 +Entitlement Type: Physical +''', ''), + ] + ), + ( + 'subscription-manager attach --pool ff8080816b8e967f016b8e99632804a6', + {'check_rc': True}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration using username and password and attach to pool ID and quantities + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'pool_ids': [{'ff8080816b8e967f016b8e99632804a6': 2}, {'ff8080816b8e967f016b8e99747107e9': 4}] + }, + { + 'id': 'test_registeration_username_password_pool_ids_quantities', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ), + ( + [ + 'subscription-manager list --available', + {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}}, + (0, + ''' ++-------------------------------------------+ + Available Subscriptions ++-------------------------------------------+ +Subscription Name: SP Smart Management (A: ADDON1) +Provides: SP Addon 1 bits +SKU: sp-with-addon-1 +Contract: 1 +Pool ID: ff8080816b8e967f016b8e99747107e9 +Provides Management: Yes +Available: 10 +Suggested: 1 +Service Type: +Roles: +Service Level: +Usage: +Add-ons: ADDON1 +Subscription Type: Standard +Starts: 25.6.2019 +Ends: 24.6.2020 +Entitlement Type: Physical + +Subscription Name: SP Server Premium (S: Premium, U: Production, R: SP Server) +Provides: SP Server Bits +SKU: sp-server-prem-prod +Contract: 0 +Pool ID: ff8080816b8e967f016b8e99632804a6 +Provides Management: Yes +Available: 5 +Suggested: 1 +Service Type: L1-L3 +Roles: SP Server +Service Level: Premium +Usage: Production +Add-ons: +Subscription Type: Standard +Starts: 06/25/19 +Ends: 06/24/20 +Entitlement Type: Physical +''', '') + ] + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99632804a6', + '--quantity', '2' + ], + {'check_rc': True}, + (0, '', '') + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99747107e9', + '--quantity', '4' + ], + {'check_rc': True}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration using username and password and attach to pool ID without quantities + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'pool_ids': ['ff8080816b8e967f016b8e99632804a6', 'ff8080816b8e967f016b8e99747107e9'] + }, + { + 'id': 'test_registeration_username_password_pool_ids', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ), + ( + [ + 'subscription-manager list --available', + {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}}, + (0, + ''' ++-------------------------------------------+ + Available Subscriptions ++-------------------------------------------+ +Subscription Name: SP Smart Management (A: ADDON1) +Provides: SP Addon 1 bits +SKU: sp-with-addon-1 +Contract: 1 +Pool ID: ff8080816b8e967f016b8e99747107e9 +Provides Management: Yes +Available: 10 +Suggested: 1 +Service Type: +Roles: +Service Level: +Usage: +Add-ons: ADDON1 +Subscription Type: Standard +Starts: 25.6.2019 +Ends: 24.6.2020 +Entitlement Type: Physical + +Subscription Name: SP Server Premium (S: Premium, U: Production, R: SP Server) +Provides: SP Server Bits +SKU: sp-server-prem-prod +Contract: 0 +Pool ID: ff8080816b8e967f016b8e99632804a6 +Provides Management: Yes +Available: 5 +Suggested: 1 +Service Type: L1-L3 +Roles: SP Server +Service Level: Premium +Usage: Production +Add-ons: +Subscription Type: Standard +Starts: 06/25/19 +Ends: 06/24/20 +Entitlement Type: Physical +''', '') + ] + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99632804a6' + ], + {'check_rc': True}, + (0, '', '') + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99747107e9' + ], + {'check_rc': True}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration using username and password and attach to pool ID (one pool) + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'pool_ids': ['ff8080816b8e967f016b8e99632804a6'] + }, + { + 'id': 'test_registeration_username_password_one_pool_id', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ), + ( + [ + 'subscription-manager list --available', + {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}}, + (0, + ''' ++-------------------------------------------+ + Available Subscriptions ++-------------------------------------------+ +Subscription Name: SP Smart Management (A: ADDON1) +Provides: SP Addon 1 bits +SKU: sp-with-addon-1 +Contract: 1 +Pool ID: ff8080816b8e967f016b8e99747107e9 +Provides Management: Yes +Available: 10 +Suggested: 1 +Service Type: +Roles: +Service Level: +Usage: +Add-ons: ADDON1 +Subscription Type: Standard +Starts: 25.6.2019 +Ends: 24.6.2020 +Entitlement Type: Physical + +Subscription Name: SP Server Premium (S: Premium, U: Production, R: SP Server) +Provides: SP Server Bits +SKU: sp-server-prem-prod +Contract: 0 +Pool ID: ff8080816b8e967f016b8e99632804a6 +Provides Management: Yes +Available: 5 +Suggested: 1 +Service Type: L1-L3 +Roles: SP Server +Service Level: Premium +Usage: Production +Add-ons: +Subscription Type: Standard +Starts: 06/25/19 +Ends: 06/24/20 +Entitlement Type: Physical +''', '') + ] + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99632804a6', + ], + {'check_rc': True}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test attaching different set of pool IDs + [ + { + 'state': 'present', + 'pool_ids': [{'ff8080816b8e967f016b8e99632804a6': 2}, {'ff8080816b8e967f016b8e99747107e9': 4}] + }, + { + 'id': 'test_attaching_different_pool_ids', + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', ''), + ), + ( + 'subscription-manager list --consumed', + {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}}, + (0, ''' ++-------------------------------------------+ + Consumed Subscriptions ++-------------------------------------------+ +Subscription Name: Multi-Attribute Stackable (4 cores, no content) +Provides: Multi-Attribute Limited Product (no content) +SKU: cores4-multiattr +Contract: 1 +Account: 12331131231 +Serial: 7807912223970164816 +Pool ID: ff8080816b8e967f016b8e995f5103b5 +Provides Management: No +Active: True +Quantity Used: 1 +Service Type: Level 3 +Roles: +Service Level: Premium +Usage: +Add-ons: +Status Details: Subscription is current +Subscription Type: Stackable +Starts: 06/25/19 +Ends: 06/24/20 +Entitlement Type: Physical +''', '') + ), + ( + [ + '/testbin/subscription-manager', + 'remove', + '--serial=7807912223970164816', + ], + {'check_rc': True}, + (0, '', '') + ), + ( + [ + 'subscription-manager list --available', + {'check_rc': True, 'environ_update': {'LANG': 'C', 'LC_ALL': 'C', 'LC_MESSAGES': 'C'}}, + (0, + ''' ++-------------------------------------------+ + Available Subscriptions ++-------------------------------------------+ +Subscription Name: SP Smart Management (A: ADDON1) +Provides: SP Addon 1 bits +SKU: sp-with-addon-1 +Contract: 1 +Pool ID: ff8080816b8e967f016b8e99747107e9 +Provides Management: Yes +Available: 10 +Suggested: 1 +Service Type: +Roles: +Service Level: +Usage: +Add-ons: ADDON1 +Subscription Type: Standard +Starts: 25.6.2019 +Ends: 24.6.2020 +Entitlement Type: Physical + +Subscription Name: SP Server Premium (S: Premium, U: Production, R: SP Server) +Provides: SP Server Bits +SKU: sp-server-prem-prod +Contract: 0 +Pool ID: ff8080816b8e967f016b8e99632804a6 +Provides Management: Yes +Available: 5 +Suggested: 1 +Service Type: L1-L3 +Roles: SP Server +Service Level: Premium +Usage: Production +Add-ons: +Subscription Type: Standard +Starts: 06/25/19 +Ends: 06/24/20 +Entitlement Type: Physical + +Subscription Name: Multi-Attribute Stackable (4 cores, no content) +Provides: Multi-Attribute Limited Product (no content) +SKU: cores4-multiattr +Contract: 1 +Pool ID: ff8080816b8e967f016b8e995f5103b5 +Provides Management: No +Available: 10 +Suggested: 1 +Service Type: Level 3 +Roles: +Service Level: Premium +Usage: +Add-ons: +Subscription Type: Stackable +Starts: 11.7.2019 +Ends: 10.7.2020 +Entitlement Type: Physical +''', '') + ] + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99632804a6', + '--quantity', '2' + ], + {'check_rc': True}, + (0, '', '') + ), + ( + [ + '/testbin/subscription-manager', + 'attach', + '--pool', 'ff8080816b8e967f016b8e99747107e9', + '--quantity', '4' + ], + {'check_rc': True}, + (0, '', '') + ) + ], + 'changed': True, + } + ] +] + + +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_redhat_subscription(mocker, capfd, patch_redhat_subscription, 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): + redhat_subscription.main() + + out, err = capfd.readouterr() + results = json.loads(out) + + assert 'changed' in results + assert results['changed'] == testcase['changed'] + if 'msg' in results: + assert results['msg'] == testcase['msg'] + + assert basic.AnsibleModule.run_command.call_count == len(testcase['run_command.calls']) + if basic.AnsibleModule.run_command.call_count: + call_args_list = [(item[0][0], item[1]) for item in basic.AnsibleModule.run_command.call_args_list] + expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']] + assert call_args_list == expected_call_args_list + + +SYSPURPOSE_TEST_CASES = [ + # Test setting syspurpose attributes (system is already registered) + # and synchronization with candlepin server + [ + { + 'state': 'present', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Production', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + 'sync': True + } + }, + { + 'id': 'test_setting_syspurpose_attributes', + 'existing_syspurpose': {}, + 'expected_syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Production', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + }, + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ), + ( + ['/testbin/subscription-manager', 'status'], + {'check_rc': False}, + (0, ''' ++-------------------------------------------+ + System Status Details ++-------------------------------------------+ +Overall Status: Current + +System Purpose Status: Matched +''', '') + ) + ], + 'changed': True, + 'msg': 'Syspurpose attributes changed.' + } + ], + # Test setting unspupported attributes + [ + { + 'state': 'present', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'syspurpose': { + 'foo': 'Bar', + 'role': 'AwesomeOS', + 'usage': 'Production', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + 'sync': True + } + }, + { + 'id': 'test_setting_syspurpose_wrong_attributes', + 'existing_syspurpose': {}, + 'expected_syspurpose': {}, + 'run_command.calls': [], + 'failed': True + } + ], + # Test setting addons not a list + [ + { + 'state': 'present', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Production', + 'service_level_agreement': 'Premium', + 'addons': 'ADDON1', + 'sync': True + } + }, + { + 'id': 'test_setting_syspurpose_addons_not_list', + 'existing_syspurpose': {}, + 'expected_syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Production', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1'] + }, + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ), + ( + ['/testbin/subscription-manager', 'status'], + {'check_rc': False}, + (0, ''' ++-------------------------------------------+ + System Status Details ++-------------------------------------------+ +Overall Status: Current + +System Purpose Status: Matched +''', '') + ) + ], + 'changed': True, + 'msg': 'Syspurpose attributes changed.' + } + ], + # Test setting syspurpose attributes (system is already registered) + # without synchronization with candlepin server. Some syspurpose attributes were set + # in the past + [ + { + 'state': 'present', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'syspurpose': { + 'role': 'AwesomeOS', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + 'sync': False + } + }, + { + 'id': 'test_changing_syspurpose_attributes', + 'existing_syspurpose': { + 'role': 'CoolOS', + 'usage': 'Production', + 'service_level_agreement': 'Super', + 'addons': [], + 'foo': 'bar' + }, + 'expected_syspurpose': { + 'role': 'AwesomeOS', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + 'foo': 'bar' + }, + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ), + ], + 'changed': True, + 'msg': 'Syspurpose attributes changed.' + } + ], + # Test trying to set syspurpose attributes (system is already registered) + # without synchronization with candlepin server. Some syspurpose attributes were set + # in the past. Syspurpose attributes are same as before + [ + { + 'state': 'present', + 'server_hostname': 'subscription.rhsm.redhat.com', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'syspurpose': { + 'role': 'AwesomeOS', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + 'sync': False + } + }, + { + 'id': 'test_not_changing_syspurpose_attributes', + 'existing_syspurpose': { + 'role': 'AwesomeOS', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + }, + 'expected_syspurpose': { + 'role': 'AwesomeOS', + 'service_level_agreement': 'Premium', + 'addons': ['ADDON1', 'ADDON2'], + }, + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (0, 'system identity: b26df632-25ed-4452-8f89-0308bfd167cb', '') + ), + ], + 'changed': False, + 'msg': 'System already registered.' + } + ], + # Test of registration using username and password with auto-attach option, when + # syspurpose attributes are set + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'auto_attach': 'true', + 'syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Testing', + 'service_level_agreement': 'Super', + 'addons': ['ADDON1'], + 'sync': False + }, + }, + { + 'id': 'test_registeration_username_password_auto_attach_syspurpose', + 'existing_syspurpose': None, + 'expected_syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Testing', + 'service_level_agreement': 'Super', + 'addons': ['ADDON1'], + }, + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--auto-attach', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], + # Test of registration using username and password with auto-attach option, when + # syspurpose attributes are set. Syspurpose attributes are also synchronized + # in this case + [ + { + 'state': 'present', + 'username': 'admin', + 'password': 'admin', + 'org_id': 'admin', + 'auto_attach': 'true', + 'syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Testing', + 'service_level_agreement': 'Super', + 'addons': ['ADDON1'], + 'sync': True + }, + }, + { + 'id': 'test_registeration_username_password_auto_attach_syspurpose_sync', + 'existing_syspurpose': None, + 'expected_syspurpose': { + 'role': 'AwesomeOS', + 'usage': 'Testing', + 'service_level_agreement': 'Super', + 'addons': ['ADDON1'], + }, + 'run_command.calls': [ + ( + ['/testbin/subscription-manager', 'identity'], + {'check_rc': False}, + (1, 'This system is not yet registered.', '') + ), + ( + [ + '/testbin/subscription-manager', + 'register', + '--org', 'admin', + '--auto-attach', + '--username', 'admin', + '--password', 'admin' + ], + {'check_rc': True, 'expand_user_and_vars': False}, + (0, '', '') + ), + ( + ['/testbin/subscription-manager', 'status'], + {'check_rc': False}, + (0, ''' ++-------------------------------------------+ + System Status Details ++-------------------------------------------+ +Overall Status: Current + +System Purpose Status: Matched +''', '') + ) + ], + 'changed': True, + 'msg': "System successfully registered to 'None'." + } + ], +] + + +SYSPURPOSE_TEST_CASES_IDS = [item[1]['id'] for item in SYSPURPOSE_TEST_CASES] + + +@pytest.mark.parametrize('patch_ansible_module, testcase', SYSPURPOSE_TEST_CASES, ids=SYSPURPOSE_TEST_CASES_IDS, indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_redhat_subscription_syspurpose(mocker, capfd, patch_redhat_subscription, patch_ansible_module, testcase, tmpdir): + """ + Run unit tests for test cases listen in SYSPURPOSE_TEST_CASES (syspurpose specific 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) + + mock_syspurpose_file = tmpdir.mkdir("syspurpose").join("syspurpose.json") + # When there there are some existing syspurpose attributes specified, then + # write them to the file first + if testcase['existing_syspurpose'] is not None: + mock_syspurpose_file.write(json.dumps(testcase['existing_syspurpose'])) + else: + mock_syspurpose_file.write("{}") + + redhat_subscription.SysPurpose.SYSPURPOSE_FILE_PATH = str(mock_syspurpose_file) + + # Try to run test case + with pytest.raises(SystemExit): + redhat_subscription.main() + + out, err = capfd.readouterr() + results = json.loads(out) + + if 'failed' in testcase: + assert results['failed'] == testcase['failed'] + else: + assert 'changed' in results + assert results['changed'] == testcase['changed'] + if 'msg' in results: + assert results['msg'] == testcase['msg'] + + mock_file_content = mock_syspurpose_file.read_text("utf-8") + current_syspurpose = json.loads(mock_file_content) + assert current_syspurpose == testcase['expected_syspurpose'] + + assert basic.AnsibleModule.run_command.call_count == len(testcase['run_command.calls']) + if basic.AnsibleModule.run_command.call_count: + call_args_list = [(item[0][0], item[1]) for item in basic.AnsibleModule.run_command.call_args_list] + expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']] + assert call_args_list == expected_call_args_list diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data.py new file mode 100644 index 000000000..da195f70a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data.py @@ -0,0 +1,278 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Andreas Botzner +# 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 json +from redis import __version__ + +from ansible_collections.community.general.plugins.modules import redis_data +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + +HAS_REDIS_USERNAME_OPTION = True +if tuple(map(int, __version__.split('.'))) < (3, 4, 0): + HAS_REDIS_USERNAME_OPTION = False + + +def test_redis_data_without_arguments(capfd): + set_module_args({}) + with pytest.raises(SystemExit) as results: + redis_data.main() + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_key(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'value': 'baz', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value='bar') + mocker.patch('redis.Redis.set', return_value=True) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['old_value'] == 'bar' + assert json.loads(out)['value'] == 'baz' + assert json.loads(out)['msg'] == 'Set key: foo' + assert json.loads(out)['changed'] is True + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_existing_key_nx(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'value': 'baz', + 'non_existing': True, + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value='bar') + mocker.patch('redis.Redis.set', return_value=None) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['old_value'] == 'bar' + assert 'value' not in json.loads(out) + assert json.loads( + out)['msg'] == 'Could not set key: foo. Key already present.' + assert json.loads(out)['changed'] is False + assert json.loads(out)['failed'] is True + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_non_existing_key_xx(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'value': 'baz', + 'existing': True, + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value=None) + mocker.patch('redis.Redis.set', return_value=None) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['old_value'] is None + assert 'value' not in json.loads(out) + assert json.loads( + out)['msg'] == 'Could not set key: foo. Key not present.' + assert json.loads(out)['changed'] is False + assert json.loads(out)['failed'] is True + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_delete_present_key(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'absent'}) + mocker.patch('redis.Redis.get', return_value='bar') + mocker.patch('redis.Redis.delete', return_value=1) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Deleted key: foo' + assert json.loads(out)['changed'] is True + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_delete_absent_key(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'absent'}) + mocker.patch('redis.Redis.delete', return_value=0) + mocker.patch('redis.Redis.get', return_value=None) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Key: foo not present' + assert json.loads(out)['changed'] is False + + +@pytest.mark.skipif(HAS_REDIS_USERNAME_OPTION, reason="Redis version > 3.4.0") +def test_redis_data_fail_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'value': 'baz', + '_ansible_check_mode': False}) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['failed'] + assert json.loads( + out)['msg'] == 'The option `username` in only supported with redis >= 3.4.0.' + + +def test_redis_data_key_no_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'value': 'baz', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value='bar') + mocker.patch('redis.Redis.set', return_value=True) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['old_value'] == 'bar' + assert json.loads(out)['value'] == 'baz' + assert json.loads(out)['msg'] == 'Set key: foo' + assert json.loads(out)['changed'] is True + + +def test_redis_delete_key_no_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'absent', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value='bar') + mocker.patch('redis.Redis.delete', return_value=1) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Deleted key: foo' + assert json.loads(out)['changed'] is True + + +def test_redis_delete_key_non_existent_key(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'absent', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value=None) + mocker.patch('redis.Redis.delete', return_value=0) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Key: foo not present' + assert json.loads(out)['changed'] is False + + +def test_redis_set_key_check_mode_nochange(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'present', + 'value': 'bar', + '_ansible_check_mode': True}) + mocker.patch('redis.Redis.get', return_value='bar') + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Key foo already has desired value' + assert json.loads(out)['value'] == 'bar' + assert not json.loads(out)['changed'] + assert json.loads(out)['old_value'] == 'bar' + + +def test_redis_set_key_check_mode_delete_nx(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'present', + 'value': 'baz', + '_ansible_check_mode': True}) + mocker.patch('redis.Redis.get', return_value=None) + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Set key: foo' + assert json.loads(out)['value'] == 'baz' + assert json.loads(out)['old_value'] is None + + +def test_redis_set_key_check_mode_delete(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'present', + 'value': 'baz', + '_ansible_check_mode': True}) + mocker.patch('redis.Redis.get', return_value='bar') + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Set key: foo' + assert json.loads(out)['value'] == 'baz' + assert json.loads(out)['old_value'] == 'bar' + + +def test_redis_set_key_check_mode(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'state': 'present', + 'value': 'baz', + '_ansible_check_mode': True}) + mocker.patch('redis.Redis.get', return_value='bar') + with pytest.raises(SystemExit): + redis_data.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['msg'] == 'Set key: foo' + assert json.loads(out)['value'] == 'baz' + assert json.loads(out)['old_value'] == 'bar' diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_incr.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_incr.py new file mode 100644 index 000000000..d819b2f7e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_incr.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Andreas Botzner +# 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 json +import redis +from redis import __version__ + +from ansible_collections.community.general.plugins.modules import redis_data_incr +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + + +HAS_REDIS_USERNAME_OPTION = True +if tuple(map(int, __version__.split('.'))) < (3, 4, 0): + HAS_REDIS_USERNAME_OPTION = False +if HAS_REDIS_USERNAME_OPTION: + from redis.exceptions import NoPermissionError + + +def test_redis_data_incr_without_arguments(capfd): + set_module_args({}) + with pytest.raises(SystemExit) as results: + redis_data_incr.main() + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_incr(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', }) + mocker.patch('redis.Redis.incr', return_value=57) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['value'] == 57.0 + assert json.loads( + out)['msg'] == 'Incremented key: foo to 57' + assert json.loads(out)['changed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_incr_int(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'increment_int': 10}) + mocker.patch('redis.Redis.incrby', return_value=57) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['value'] == 57.0 + assert json.loads( + out)['msg'] == 'Incremented key: foo by 10 to 57' + assert json.loads(out)['changed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_inc_float(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'increment_float': '5.5'}) + mocker.patch('redis.Redis.incrbyfloat', return_value=57.45) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['value'] == 57.45 + assert json.loads( + out)['msg'] == 'Incremented key: foo by 5.5 to 57.45' + assert json.loads(out)['changed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_incr_float_wrong_value(capfd): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + 'increment_float': 'not_a_number'}) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['failed'] + + +@pytest.mark.skipif(HAS_REDIS_USERNAME_OPTION, reason="Redis version > 3.4.0") +def test_redis_data_incr_fail_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': False}) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['failed'] + assert json.loads( + out)['msg'] == 'The option `username` in only supported with redis >= 3.4.0.' + + +def test_redis_data_incr_no_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', }) + mocker.patch('redis.Redis.incr', return_value=57) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['value'] == 57.0 + assert json.loads( + out)['msg'] == 'Incremented key: foo to 57' + assert json.loads(out)['changed'] + + +def test_redis_data_incr_float_no_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + 'increment_float': '5.5'}) + mocker.patch('redis.Redis.incrbyfloat', return_value=57.45) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['value'] == 57.45 + assert json.loads( + out)['msg'] == 'Incremented key: foo by 5.5 to 57.45' + assert json.loads(out)['changed'] + + +def test_redis_data_incr_check_mode(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': True}) + mocker.patch('redis.Redis.get', return_value=10) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['value'] == 11.0 + assert json.loads(out)['msg'] == 'Incremented key: foo by 1 to 11.0' + assert not json.loads(out)['changed'] + + +def test_redis_data_incr_check_mode_not_incrementable(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': True}) + mocker.patch('redis.Redis.get', return_value='bar') + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['failed'] + assert json.loads(out)[ + 'msg'] == "Value: bar of key: foo is not incrementable(int or float)" + assert 'value' not in json.loads(out) + assert not json.loads(out)['changed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_incr_check_mode_permissions(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': True}) + redis.Redis.get = mocker.Mock(side_effect=NoPermissionError( + "this user has no permissions to run the 'get' command or its subcommand")) + with pytest.raises(SystemExit): + redis_data_incr.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['failed'] + assert json.loads(out)['msg'].startswith( + 'Failed to get value of key: foo with exception:') + assert 'value' not in json.loads(out) + assert not json.loads(out)['changed'] diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_info.py new file mode 100644 index 000000000..302e003bf --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_data_info.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2021, Andreas Botzner +# 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 json +from redis import __version__ + +from ansible_collections.community.general.plugins.modules import ( + redis_data_info) +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + + +HAS_REDIS_USERNAME_OPTION = True +if tuple(map(int, __version__.split('.'))) < (3, 4, 0): + HAS_REDIS_USERNAME_OPTION = False + + +def test_redis_data_info_without_arguments(capfd): + set_module_args({}) + with pytest.raises(SystemExit): + redis_data_info.main() + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_info_existing_key(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value='bar') + with pytest.raises(SystemExit): + redis_data_info.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['exists'] + assert json.loads(out)['value'] == 'bar' + + +@pytest.mark.skipif(not HAS_REDIS_USERNAME_OPTION, reason="Redis version < 3.4.0") +def test_redis_data_info_absent_key(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value=None) + with pytest.raises(SystemExit): + redis_data_info.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert not json.loads(out)['exists'] + assert 'value' not in json.loads(out) + + +@pytest.mark.skipif(HAS_REDIS_USERNAME_OPTION, reason="Redis version > 3.4.0") +def test_redis_data_fail_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_user': 'root', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': False}) + with pytest.raises(SystemExit): + redis_data_info.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['failed'] + assert json.loads( + out)['msg'] == 'The option `username` in only supported with redis >= 3.4.0.' + + +@pytest.mark.skipif(HAS_REDIS_USERNAME_OPTION, reason="Redis version > 3.4.0") +def test_redis_data_info_absent_key_no_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value=None) + with pytest.raises(SystemExit): + redis_data_info.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert not json.loads(out)['exists'] + assert 'value' not in json.loads(out) + + +@pytest.mark.skipif(HAS_REDIS_USERNAME_OPTION, reason="Redis version > 3.4.0") +def test_redis_data_info_existing_key_no_username(capfd, mocker): + set_module_args({'login_host': 'localhost', + 'login_password': 'secret', + 'key': 'foo', + '_ansible_check_mode': False}) + mocker.patch('redis.Redis.get', return_value='bar') + with pytest.raises(SystemExit): + redis_data_info.main() + out, err = capfd.readouterr() + print(out) + assert not err + assert json.loads(out)['exists'] + assert json.loads(out)['value'] == 'bar' 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 new file mode 100644 index 000000000..8b30a2316 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2020, Pavlo Bashynskyi (@levonet) +# 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, MagicMock +from ansible_collections.community.general.plugins.modules import redis_info +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class FakeRedisClient(MagicMock): + + def ping(self): + pass + + def info(self): + return {'redis_version': '999.999.999'} + + +class FakeRedisClientFail(MagicMock): + + def ping(self): + raise Exception('Test Error') + + def info(self): + pass + + +class TestRedisInfoModule(ModuleTestCase): + + def setUp(self): + super(TestRedisInfoModule, self).setUp() + redis_info.HAS_REDIS_PACKAGE = True + self.module = redis_info + + def tearDown(self): + super(TestRedisInfoModule, self).tearDown() + + def patch_redis_client(self, **kwds): + return patch('ansible_collections.community.general.plugins.modules.redis_info.redis_client', autospec=True, **kwds) + + def test_without_parameters(self): + """Test without parameters""" + with self.patch_redis_client(side_effect=FakeRedisClient) as redis_client: + with self.assertRaises(AnsibleExitJson) as result: + 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(result.exception.args[0]['info']['redis_version'], '999.999.999') + + def test_with_parameters(self): + """Test with all 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' + }) + self.module.main() + self.assertEqual(redis_client.call_count, 1) + self.assertEqual(redis_client.call_args, ({'host': 'test', 'port': 1234, 'password': 'PASS'},)) + self.assertEqual(result.exception.args[0]['info']['redis_version'], '999.999.999') + + def test_with_fail_client(self): + """Test failure message""" + with self.patch_redis_client(side_effect=FakeRedisClientFail) as redis_client: + with self.assertRaises(AnsibleFailJson) as result: + set_module_args({}) + self.module.main() + self.assertEqual(redis_client.call_count, 1) + self.assertEqual(result.exception.args[0]['msg'], 'unable to connect to database: Test Error') diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_channel.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_channel.py new file mode 100644 index 000000000..fd3bdc5fe --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_channel.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017 Pierre-Louis Bonicoli +# 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 import rhn_channel + +from .rhn_conftest import mock_request # noqa: F401, pylint: disable=unused-import + +import pytest + + +pytestmark = pytest.mark.usefixtures('patch_ansible_module') + + +@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) +def test_without_required_parameters(capfd): + with pytest.raises(SystemExit): + rhn_channel.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'missing required arguments' in results['msg'] + + +TESTED_MODULE = rhn_channel.__name__ +TEST_CASES = [ + [ + # add channel already added, check that result isn't changed + { + 'name': 'rhel-x86_64-server-6', + 'sysname': 'server01', + 'url': 'https://rhn.redhat.com/rpc/api', + 'user': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('system.listUserSystems', + [[{'last_checkin': '2017-08-06 19:49:52.0', 'id': '0123456789', 'name': 'server01'}]]), + ('channel.software.listSystemChannels', + [[{'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'}]]), + ('auth.logout', [1]), + ], + 'changed': False, + 'msg': 'Channel rhel-x86_64-server-6 already exists', + } + ], + [ + # add channel, check that result is changed + { + 'name': 'rhel-x86_64-server-6-debuginfo', + 'sysname': 'server01', + 'url': 'https://rhn.redhat.com/rpc/api', + 'user': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('system.listUserSystems', + [[{'last_checkin': '2017-08-06 19:49:52.0', 'id': '0123456789', 'name': 'server01'}]]), + ('channel.software.listSystemChannels', + [[{'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'}]]), + ('channel.software.listSystemChannels', + [[{'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'}]]), + ('system.setChildChannels', [1]), + ('auth.logout', [1]), + ], + 'changed': True, + 'msg': 'Channel rhel-x86_64-server-6-debuginfo added', + } + ], + [ + # remove inexistent channel, check that result isn't changed + { + 'name': 'rhel-x86_64-server-6-debuginfo', + 'state': 'absent', + 'sysname': 'server01', + 'url': 'https://rhn.redhat.com/rpc/api', + 'user': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('system.listUserSystems', + [[{'last_checkin': '2017-08-06 19:49:52.0', 'id': '0123456789', 'name': 'server01'}]]), + ('channel.software.listSystemChannels', + [[{'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'}]]), + ('auth.logout', [1]), + ], + 'changed': False, + 'msg': 'Not subscribed to channel rhel-x86_64-server-6-debuginfo.', + } + ], + [ + # remove channel, check that result is changed + { + 'name': 'rhel-x86_64-server-6-debuginfo', + 'state': 'absent', + 'sysname': 'server01', + 'url': 'https://rhn.redhat.com/rpc/api', + 'user': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('system.listUserSystems', + [[{'last_checkin': '2017-08-06 19:49:52.0', 'id': '0123456789', 'name': 'server01'}]]), + ('channel.software.listSystemChannels', [[ + {'channel_name': 'RHEL Server Debuginfo (v.6 for x86_64)', 'channel_label': 'rhel-x86_64-server-6-debuginfo'}, + {'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'} + ]]), + ('channel.software.listSystemChannels', [[ + {'channel_name': 'RHEL Server Debuginfo (v.6 for x86_64)', 'channel_label': 'rhel-x86_64-server-6-debuginfo'}, + {'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'} + ]]), + ('system.setChildChannels', [1]), + ('auth.logout', [1]), + ], + 'changed': True, + 'msg': 'Channel rhel-x86_64-server-6-debuginfo removed' + } + ] +] + + +@pytest.mark.parametrize('patch_ansible_module, testcase', TEST_CASES, indirect=['patch_ansible_module']) +def test_rhn_channel(capfd, mocker, testcase, mock_request): + """Check 'msg' and 'changed' results""" + + with pytest.raises(SystemExit): + rhn_channel.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert results['changed'] == testcase['changed'] + assert results['msg'] == testcase['msg'] + assert not testcase['calls'] # all calls should have been consumed diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_register.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_register.py new file mode 100644 index 000000000..1394c07b6 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhn_register.py @@ -0,0 +1,293 @@ +# 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 +import os + +from ansible_collections.community.general.tests.unit.compat.mock import mock_open +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_native +import ansible.module_utils.six +from ansible.module_utils.six.moves import xmlrpc_client +from ansible_collections.community.general.plugins.modules import rhn_register + +from .rhn_conftest import mock_request # noqa: F401, pylint: disable=unused-import + +import pytest + + +SYSTEMID = """ + + + + +system_id +ID-123456789 + + + + +""" + + +def skipWhenAllModulesMissing(modules): + """Skip the decorated test unless one of modules is available.""" + for module in modules: + try: + __import__(module) + return False + except ImportError: + continue + + return True + + +orig_import = __import__ + + +@pytest.fixture +def import_libxml(mocker): + def mock_import(name, *args, **kwargs): + if name in ['libxml2', 'libxml']: + raise ImportError() + else: + return orig_import(name, *args, **kwargs) + + if ansible.module_utils.six.PY3: + mocker.patch('builtins.__import__', side_effect=mock_import) + else: + mocker.patch('__builtin__.__import__', side_effect=mock_import) + + +@pytest.fixture +def patch_rhn(mocker): + load_config_return = { + 'serverURL': 'https://xmlrpc.rhn.redhat.com/XMLRPC', + 'systemIdPath': '/etc/sysconfig/rhn/systemid' + } + + mocker.patch.object(rhn_register.Rhn, 'load_config', return_value=load_config_return) + mocker.patch.object(rhn_register, 'HAS_UP2DATE_CLIENT', mocker.PropertyMock(return_value=True)) + + +@pytest.mark.skipif(skipWhenAllModulesMissing(['libxml2', 'libxml']), reason='none are available: libxml2, libxml') +def test_systemid_with_requirements(capfd, mocker, patch_rhn): + """Check 'msg' and 'changed' results""" + + mocker.patch.object(rhn_register.Rhn, 'enable') + mock_isfile = mocker.patch('os.path.isfile', return_value=True) + mocker.patch('ansible_collections.community.general.plugins.modules.rhn_register.open', mock_open(read_data=SYSTEMID), create=True) + rhn = rhn_register.Rhn() + assert '123456789' == to_native(rhn.systemid) + + +@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_systemid_requirements_missing(capfd, mocker, patch_rhn, import_libxml): + """Check that missing dependencies are detected""" + + mocker.patch('os.path.isfile', return_value=True) + mocker.patch('ansible_collections.community.general.plugins.modules.rhn_register.open', mock_open(read_data=SYSTEMID), create=True) + + with pytest.raises(SystemExit): + rhn_register.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'Missing arguments' in results['msg'] + + +@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_without_required_parameters(capfd, patch_rhn): + """Failure must occurs when all parameters are missing""" + + with pytest.raises(SystemExit): + rhn_register.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'Missing arguments' in results['msg'] + + +TESTED_MODULE = rhn_register.__name__ +TEST_CASES = [ + [ + # Registering an unregistered host with channels + { + 'channels': 'rhel-x86_64-server-6', + 'username': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('channel.software.listSystemChannels', + [[{'channel_name': 'Red Hat Enterprise Linux Server (v. 6 for 64-bit x86_64)', 'channel_label': 'rhel-x86_64-server-6'}]]), + ('channel.software.setSystemChannels', [1]), + ('auth.logout', [1]), + ], + 'is_registered': False, + 'is_registered.call_count': 1, + 'enable.call_count': 1, + 'systemid.call_count': 2, + 'changed': True, + 'msg': "System successfully registered to 'rhn.redhat.com'.", + 'run_command.call_count': 1, + 'run_command.call_args': '/usr/sbin/rhnreg_ks', + 'request_called': True, + 'unlink.call_count': 0, + } + ], + [ + # Registering an unregistered host without channels + { + 'activationkey': 'key', + 'username': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ], + 'is_registered': False, + 'is_registered.call_count': 1, + 'enable.call_count': 1, + 'systemid.call_count': 0, + 'changed': True, + 'msg': "System successfully registered to 'rhn.redhat.com'.", + 'run_command.call_count': 1, + 'run_command.call_args': '/usr/sbin/rhnreg_ks', + 'request_called': False, + 'unlink.call_count': 0, + } + ], + [ + # Register an host already registered, check that result is unchanged + { + 'activationkey': 'key', + 'username': 'user', + 'password': 'pass', + }, + { + 'calls': [ + ], + 'is_registered': True, + 'is_registered.call_count': 1, + 'enable.call_count': 0, + 'systemid.call_count': 0, + 'changed': False, + 'msg': 'System already registered.', + 'run_command.call_count': 0, + 'request_called': False, + 'unlink.call_count': 0, + }, + ], + [ + # Unregister an host, check that result is changed + { + 'activationkey': 'key', + 'username': 'user', + 'password': 'pass', + 'state': 'absent', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('system.deleteSystems', [1]), + ('auth.logout', [1]), + ], + 'is_registered': True, + 'is_registered.call_count': 1, + 'enable.call_count': 0, + 'systemid.call_count': 1, + 'changed': True, + 'msg': 'System successfully unregistered from rhn.redhat.com.', + 'run_command.call_count': 0, + 'request_called': True, + 'unlink.call_count': 1, + } + ], + [ + # Unregister a unregistered host (systemid missing) locally, check that result is unchanged + { + 'activationkey': 'key', + 'username': 'user', + 'password': 'pass', + 'state': 'absent', + }, + { + 'calls': [], + 'is_registered': False, + 'is_registered.call_count': 1, + 'enable.call_count': 0, + 'systemid.call_count': 0, + 'changed': False, + 'msg': 'System already unregistered.', + 'run_command.call_count': 0, + 'request_called': False, + 'unlink.call_count': 0, + } + + ], + [ + # Unregister an unknown host (an host with a systemid available locally, check that result contains failed + { + 'activationkey': 'key', + 'username': 'user', + 'password': 'pass', + 'state': 'absent', + }, + { + 'calls': [ + ('auth.login', ['X' * 43]), + ('system.deleteSystems', xmlrpc_client.Fault(1003, 'The following systems were NOT deleted: 123456789')), + ('auth.logout', [1]), + ], + 'is_registered': True, + 'is_registered.call_count': 1, + 'enable.call_count': 0, + 'systemid.call_count': 1, + 'failed': True, + 'msg': "Failed to unregister: ", + 'run_command.call_count': 0, + 'request_called': True, + 'unlink.call_count': 0, + } + ], +] + + +@pytest.mark.parametrize('patch_ansible_module, testcase', TEST_CASES, indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_register_parameters(mocker, capfd, mock_request, patch_rhn, testcase): + # successful execution, no output + mocker.patch.object(basic.AnsibleModule, 'run_command', return_value=(0, '', '')) + mock_is_registered = mocker.patch.object(rhn_register.Rhn, 'is_registered', mocker.PropertyMock(return_value=testcase['is_registered'])) + mocker.patch.object(rhn_register.Rhn, 'enable') + mock_systemid = mocker.patch.object(rhn_register.Rhn, 'systemid', mocker.PropertyMock(return_value=12345)) + mocker.patch('os.unlink', return_value=True) + + with pytest.raises(SystemExit): + rhn_register.main() + + assert basic.AnsibleModule.run_command.call_count == testcase['run_command.call_count'] + if basic.AnsibleModule.run_command.call_count: + assert basic.AnsibleModule.run_command.call_args[0][0][0] == testcase['run_command.call_args'] + + assert mock_is_registered.call_count == testcase['is_registered.call_count'] + assert rhn_register.Rhn.enable.call_count == testcase['enable.call_count'] + assert mock_systemid.call_count == testcase['systemid.call_count'] + assert xmlrpc_client.Transport.request.called == testcase['request_called'] + assert os.unlink.call_count == testcase['unlink.call_count'] + + out, err = capfd.readouterr() + results = json.loads(out) + assert results.get('changed') == testcase.get('changed') + assert results.get('failed') == testcase.get('failed') + assert results['msg'] == testcase['msg'] + assert not testcase['calls'] # all calls should have been consumed 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 new file mode 100644 index 000000000..c5696962b --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py @@ -0,0 +1,148 @@ +# Copyright (c) 2018, Sean Myers +# 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 call, patch +from ansible_collections.community.general.plugins.modules import rhsm_release +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args) + + +class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase): + module = rhsm_release + + def setUp(self): + super(RhsmRepositoryReleaseModuleTestCase, self).setUp() + + # Mainly interested that the subscription-manager calls are right + # based on the module args, so patch out run_command in the module. + # returns (rc, out, err) structure + self.mock_run_command = patch('ansible_collections.community.general.plugins.modules.rhsm_release.' + 'AnsibleModule.run_command') + self.module_main_command = self.mock_run_command.start() + + # Module does a get_bin_path check before every run_command call + self.mock_get_bin_path = patch('ansible_collections.community.general.plugins.modules.rhsm_release.' + 'AnsibleModule.get_bin_path') + self.get_bin_path = self.mock_get_bin_path.start() + self.get_bin_path.return_value = '/testbin/subscription-manager' + + # subscription-manager needs to be run as root + self.mock_os_getuid = patch('ansible_collections.community.general.plugins.modules.rhsm_release.' + 'os.getuid') + self.os_getuid = self.mock_os_getuid.start() + self.os_getuid.return_value = 0 + + def tearDown(self): + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + self.mock_os_getuid.stop() + super(RhsmRepositoryReleaseModuleTestCase, self).tearDown() + + def module_main(self, exit_exc): + with self.assertRaises(exit_exc) as exc: + self.module.main() + return exc.exception.args[0] + + def test_release_set(self): + # test that the module attempts to change the release when the current + # release is not the same as the user-specific target release + set_module_args({'release': '7.5'}) + self.module_main_command.side_effect = [ + # first call, get_release: returns different version so set_release is called + (0, '7.4', ''), + # second call, set_release: just needs to exit with 0 rc + (0, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_release_set_idempotent(self): + # test that the module does not attempt to change the release when + # the current release matches the user-specified target release + set_module_args({'release': '7.5'}) + self.module_main_command.side_effect = [ + # first call, get_release: returns same version, set_release is not called + (0, '7.5', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_release_unset(self): + # test that the module attempts to change the release when the current + # release is not the same as the user-specific target release + set_module_args({'release': None}) + self.module_main_command.side_effect = [ + # first call, get_release: returns version so set_release is called + (0, '7.5', ''), + # second call, set_release: just needs to exit with 0 rc + (0, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_release_unset_idempotent(self): + # test that the module attempts to change the release when the current + # release is not the same as the user-specific target release + set_module_args({'release': None}) + self.module_main_command.side_effect = [ + # first call, get_release: returns no version, set_release is not called + (0, 'Release not set', ''), + ] + + result = self.module_main(AnsibleExitJson) + + 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), + ]) + + def test_release_insane(self): + # test that insane values for release trigger fail_json + insane_value = 'this is an insane release value' + set_module_args({'release': insane_value}) + + result = self.module_main(AnsibleFailJson) + + # also ensure that the fail msg includes the insane value + self.assertIn(insane_value, result['msg']) + + def test_release_matcher(self): + # throw a few values at the release matcher -- only sane_values should match + sane_values = ['1Server', '1Client', '10Server', '1.10', '10.0', '9'] + insane_values = [ + '6server', # lowercase 's' + '100Server', # excessively long 'x' component + '100.100', # excessively long 'x' and 'y' components + '+.-', # illegal characters + ] + + matches = self.module.release_matcher.findall(' '.join(sane_values + insane_values)) + + # matches should be returned in the same order they were parsed, + # so sorting shouldn't be necessary here + self.assertEqual(matches, sane_values) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_rpm_ostree_pkg.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_rpm_ostree_pkg.py new file mode 100644 index 000000000..4888b6402 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_rpm_ostree_pkg.py @@ -0,0 +1,108 @@ +# +# Copyright (c) 2021, Abhijeet Kasurde +# 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 call, patch +from ansible_collections.community.general.plugins.modules import rpm_ostree_pkg +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args) + + +class RpmOSTreeModuleTestCase(ModuleTestCase): + module = rpm_ostree_pkg + + def setUp(self): + super(RpmOSTreeModuleTestCase, self).setUp() + ansible_module_path = "ansible_collections.community.general.plugins.modules.rpm_ostree_pkg.AnsibleModule" + self.mock_run_command = patch('%s.run_command' % ansible_module_path) + self.module_main_command = self.mock_run_command.start() + self.mock_get_bin_path = patch('%s.get_bin_path' % ansible_module_path) + self.get_bin_path = self.mock_get_bin_path.start() + self.get_bin_path.return_value = '/testbin/rpm-ostree' + + def tearDown(self): + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + super(RpmOSTreeModuleTestCase, self).tearDown() + + def module_main(self, exit_exc): + with self.assertRaises(exit_exc) as exc: + self.module.main() + return exc.exception.args[0] + + def test_present(self): + set_module_args({'name': 'nfs-utils', 'state': 'present'}) + self.module_main_command.side_effect = [ + (0, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'install', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_present_unchanged(self): + set_module_args({'name': 'nfs-utils', 'state': 'present'}) + self.module_main_command.side_effect = [ + (77, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertFalse(result['changed']) + self.assertEqual(0, result['rc']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'install', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_present_failed(self): + set_module_args({'name': 'nfs-utils', 'state': 'present'}) + self.module_main_command.side_effect = [ + (1, '', ''), + ] + + result = self.module_main(AnsibleFailJson) + + self.assertFalse(result['changed']) + self.assertEqual(1, result['rc']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'install', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_absent(self): + set_module_args({'name': 'nfs-utils', 'state': 'absent'}) + self.module_main_command.side_effect = [ + (0, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'uninstall', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_absent_failed(self): + set_module_args({'name': 'nfs-utils', 'state': 'absent'}) + self.module_main_command.side_effect = [ + (1, '', ''), + ] + + result = self.module_main(AnsibleFailJson) + + self.assertFalse(result['changed']) + self.assertEqual(1, result['rc']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'uninstall', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) 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 new file mode 100644 index 000000000..34c97c4a8 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_sap_task_list_execute.py @@ -0,0 +1,91 @@ +# 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 new file mode 100644 index 000000000..bec9cf886 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_sapcar_extract.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2021, Rainer Leber (@rainerleber) +# 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_scaleway_compute_private_network.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_compute_private_network.py new file mode 100644 index 000000000..df6fd91a4 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_compute_private_network.py @@ -0,0 +1,180 @@ +# Copyright (c) 2019, 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 os +import json +import pytest + + +from ansible_collections.community.general.plugins.modules import scaleway_compute_private_network +from ansible_collections.community.general.plugins.module_utils.scaleway import Scaleway, Response +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args +from ansible_collections.community.general.tests.unit.compat.mock import patch + + +def response_without_nics(): + info = {"status": 200, + "body": '{ "private_nics": []}' + } + return Response(None, info) + + +def response_with_nics(): + info = {"status": 200, + "body": ('{ "private_nics": [{' + '"id": "c123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"private_network_id": "b589b4cd-ef5g-678h-90i1-jk2345678l90",' + '"server_id": "c004b4cd-ef5g-678h-90i1-jk2345678l90",' + '"mac_address": "02:00:00:00:12:23",' + '"state": "available",' + '"creation_date": "2022-03-30T06:25:28.155973+00:00",' + '"modification_date": "2022-03-30T06:25:28.155973+00:00",' + '"zone": "fr-par-1"' + '}]}' + ) + } + return Response(None, info) + + +def response_when_add_nics(): + info = {"status": 200, + "body": ('{ "private_nics": {' + '"id": "c123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"private_network_id": "b589b4cd-ef5g-678h-90i1-jk2345678l90",' + '"server_id": "c004b4cd-ef5g-678h-90i1-jk2345678l90",' + '"mac_address": "02:00:00:00:12:23",' + '"state": "available",' + '"creation_date": "2022-03-30T06:25:28.155973+00:00",' + '"modification_date": "2022-03-30T06:25:28.155973+00:00",' + '"zone": "fr-par-1"' + '}}' + ) + } + return Response(None, info) + + +def response_remove_nics(): + info = {"status": 200} + return Response(None, info) + + +def test_scaleway_private_network_without_arguments(capfd): + set_module_args({}) + with pytest.raises(SystemExit) as results: + scaleway_compute_private_network.main() + out, err = capfd.readouterr() + + assert not err + assert json.loads(out)['failed'] + + +def test_scaleway_add_nic(capfd): + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + pnid = 'b589b4cd-ef5g-678h-90i1-jk2345678l90' + cid = 'c004b4cd-ef5g-678h-90i1-jk2345678l90' + url = 'servers/' + cid + '/private_nics' + + set_module_args({"project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "state": "present", + "region": "par1", + "compute_id": cid, + "private_network_id": pnid + }) + + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_without_nics() + with patch.object(Scaleway, 'post') as mock_scw_post: + mock_scw_post.return_value = response_when_add_nics() + with pytest.raises(SystemExit) as results: + scaleway_compute_private_network.main() + mock_scw_post.assert_any_call(path=url, data={"private_network_id": pnid}) + mock_scw_get.assert_any_call(url) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + assert not err + assert json.loads(out)['changed'] + + +def test_scaleway_add_existing_nic(capfd): + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + pnid = 'b589b4cd-ef5g-678h-90i1-jk2345678l90' + cid = 'c004b4cd-ef5g-678h-90i1-jk2345678l90' + url = 'servers/' + cid + '/private_nics' + + set_module_args({"project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "state": "present", + "region": "par1", + "compute_id": cid, + "private_network_id": pnid + }) + + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_nics() + with pytest.raises(SystemExit) as results: + scaleway_compute_private_network.main() + mock_scw_get.assert_any_call(url) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + assert not err + assert not json.loads(out)['changed'] + + +def test_scaleway_remove_existing_nic(capfd): + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + pnid = 'b589b4cd-ef5g-678h-90i1-jk2345678l90' + cid = 'c004b4cd-ef5g-678h-90i1-jk2345678l90' + nicid = 'c123b4cd-ef5g-678h-90i1-jk2345678l90' + url = 'servers/' + cid + '/private_nics' + urlremove = 'servers/' + cid + '/private_nics/' + nicid + + set_module_args({"project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "state": "absent", + "region": "par1", + "compute_id": cid, + "private_network_id": pnid + }) + + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_nics() + with patch.object(Scaleway, 'delete') as mock_scw_delete: + mock_scw_delete.return_value = response_remove_nics() + with pytest.raises(SystemExit) as results: + scaleway_compute_private_network.main() + mock_scw_delete.assert_any_call(urlremove) + mock_scw_get.assert_any_call(url) + + out, err = capfd.readouterr() + + del os.environ['SCW_API_TOKEN'] + assert not err + assert json.loads(out)['changed'] + + +def test_scaleway_remove_absent_nic(capfd): + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + pnid = 'b589b4cd-ef5g-678h-90i1-jk2345678l90' + cid = 'c004b4cd-ef5g-678h-90i1-jk2345678l90' + url = 'servers/' + cid + '/private_nics' + + set_module_args({"project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "state": "absent", + "region": "par1", + "compute_id": cid, + "private_network_id": pnid + }) + + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_without_nics() + with pytest.raises(SystemExit) as results: + scaleway_compute_private_network.main() + mock_scw_get.assert_any_call(url) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + assert not err + assert not json.loads(out)['changed'] diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_private_network.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_private_network.py new file mode 100644 index 000000000..21805d3db --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_scaleway_private_network.py @@ -0,0 +1,198 @@ + +# Copyright (c) 2019, 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 os +import json +import pytest + + +from ansible_collections.community.general.plugins.modules import scaleway_private_network +from ansible_collections.community.general.plugins.module_utils.scaleway import Scaleway, Response +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args +from ansible_collections.community.general.tests.unit.compat.mock import patch + + +def response_with_zero_network(): + info = {"status": 200, + "body": '{ "private_networks": [], "total_count": 0}' + } + return Response(None, info) + + +def response_with_new_network(): + info = {"status": 200, + "body": ('{ "private_networks": [{' + '"id": "c123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"name": "new_network_name",' + '"tags": ["tag1"]' + '}], "total_count": 1}' + ) + } + return Response(None, info) + + +def response_create_new(): + info = {"status": 200, + "body": ('{"id": "c123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"name": "anoter_network",' + '"organization_id": "a123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"project_id": "a123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"zone": "fr-par-2",' + '"tags": ["tag1"],' + '"created_at": "2019-04-18T15:27:24.177854Z",' + '"updated_at": "2019-04-18T15:27:24.177854Z"}' + ) + } + return Response(None, info) + + +def response_create_new_newtag(): + info = {"status": 200, + "body": ('{"id": "c123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"name": "anoter_network",' + '"organization_id": "a123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"project_id": "a123b4cd-ef5g-678h-90i1-jk2345678l90",' + '"zone": "fr-par-2",' + '"tags": ["newtag"],' + '"created_at": "2019-04-18T15:27:24.177854Z",' + '"updated_at": "2020-01-18T15:27:24.177854Z"}' + ) + } + return Response(None, info) + + +def response_delete(): + info = {"status": 204} + return Response(None, info) + + +def test_scaleway_private_network_without_arguments(capfd): + set_module_args({}) + with pytest.raises(SystemExit) as results: + scaleway_private_network.main() + out, err = capfd.readouterr() + + assert not err + assert json.loads(out)['failed'] + + +def test_scaleway_create_pn(capfd): + set_module_args({"state": "present", + "project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "region": "par2", + "name": "new_network_name", + "tags": ["tag1"] + }) + + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_zero_network() + with patch.object(Scaleway, 'post') as mock_scw_post: + mock_scw_post.return_value = response_create_new() + with pytest.raises(SystemExit) as results: + scaleway_private_network.main() + mock_scw_post.assert_any_call(path='private-networks/', data={'name': 'new_network_name', + 'project_id': 'a123b4cd-ef5g-678h-90i1-jk2345678l90', + 'tags': ['tag1']}) + mock_scw_get.assert_any_call('private-networks', params={'name': 'new_network_name', 'order_by': 'name_asc', 'page': 1, 'page_size': 10}) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + + +def test_scaleway_existing_pn(capfd): + set_module_args({"state": "present", + "project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "region": "par2", + "name": "new_network_name", + "tags": ["tag1"] + }) + + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_new_network() + with pytest.raises(SystemExit) as results: + scaleway_private_network.main() + mock_scw_get.assert_any_call('private-networks', params={'name': 'new_network_name', 'order_by': 'name_asc', 'page': 1, 'page_size': 10}) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + + assert not err + assert not json.loads(out)['changed'] + + +def test_scaleway_add_tag_pn(capfd): + set_module_args({"state": "present", + "project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "region": "par2", + "name": "new_network_name", + "tags": ["newtag"] + }) + + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_new_network() + with patch.object(Scaleway, 'patch') as mock_scw_patch: + mock_scw_patch.return_value = response_create_new_newtag() + with pytest.raises(SystemExit) as results: + scaleway_private_network.main() + mock_scw_patch.assert_any_call(path='private-networks/c123b4cd-ef5g-678h-90i1-jk2345678l90', data={'name': 'new_network_name', 'tags': ['newtag']}) + mock_scw_get.assert_any_call('private-networks', params={'name': 'new_network_name', 'order_by': 'name_asc', 'page': 1, 'page_size': 10}) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + + assert not err + assert json.loads(out)['changed'] + + +def test_scaleway_remove_pn(capfd): + set_module_args({"state": "absent", + "project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "region": "par2", + "name": "new_network_name", + "tags": ["newtag"] + }) + + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_new_network() + with patch.object(Scaleway, 'delete') as mock_scw_delete: + mock_scw_delete.return_value = response_delete() + with pytest.raises(SystemExit) as results: + scaleway_private_network.main() + mock_scw_delete.assert_any_call('private-networks/c123b4cd-ef5g-678h-90i1-jk2345678l90') + mock_scw_get.assert_any_call('private-networks', params={'name': 'new_network_name', 'order_by': 'name_asc', 'page': 1, 'page_size': 10}) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + + assert not err + assert json.loads(out)['changed'] + + +def test_scaleway_absent_pn_not_exists(capfd): + set_module_args({"state": "absent", + "project": "a123b4cd-ef5g-678h-90i1-jk2345678l90", + "region": "par2", + "name": "new_network_name", + "tags": ["newtag"] + }) + + os.environ['SCW_API_TOKEN'] = 'notrealtoken' + with patch.object(Scaleway, 'get') as mock_scw_get: + mock_scw_get.return_value = response_with_zero_network() + with pytest.raises(SystemExit) as results: + scaleway_private_network.main() + mock_scw_get.assert_any_call('private-networks', params={'name': 'new_network_name', 'order_by': 'name_asc', 'page': 1, 'page_size': 10}) + + out, err = capfd.readouterr() + del os.environ['SCW_API_TOKEN'] + + assert not err + assert not json.loads(out)['changed'] 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 new file mode 100644 index 000000000..ab4405baa --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py @@ -0,0 +1,203 @@ +# 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 +import pytest +from ansible_collections.community.general.tests.unit.compat.mock import Mock, patch +from ansible_collections.community.general.plugins.modules import slack +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args + + +class TestSlackModule(ModuleTestCase): + + def setUp(self): + super(TestSlackModule, self).setUp() + self.module = slack + + def tearDown(self): + super(TestSlackModule, self).tearDown() + + @pytest.fixture + def fetch_url_mock(self, mocker): + return mocker.patch('ansible.module_utils.notification.slack.fetch_url') + + 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_invalid_old_token(self): + """Failure if there is an old style token""" + set_module_args({ + 'token': 'test', + }) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_successful_message(self): + """tests sending a message. This is example 1 from the docs""" + set_module_args({ + 'token': 'XXXX/YYYY/ZZZZ', + 'msg': 'test' + }) + + with patch.object(slack, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 200}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['username'] == "Ansible" + assert call_data['text'] == "test" + assert fetch_url_mock.call_args[1]['url'] == "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ" + + def test_failed_message(self): + """tests failing to send a message""" + + set_module_args({ + 'token': 'XXXX/YYYY/ZZZZ', + 'msg': 'test' + }) + + with patch.object(slack, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'test'}) + with self.assertRaises(AnsibleFailJson): + self.module.main() + + def test_message_with_thread(self): + """tests sending a message with a thread""" + set_module_args({ + 'token': 'XXXX/YYYY/ZZZZ', + 'msg': 'test', + 'thread_id': '100.00' + }) + + with patch.object(slack, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 200}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['username'] == "Ansible" + assert call_data['text'] == "test" + assert call_data['thread_ts'] == '100.00' + assert fetch_url_mock.call_args[1]['url'] == "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ" + + # https://github.com/ansible-collections/community.general/issues/1097 + def test_ts_in_message_does_not_cause_edit(self): + set_module_args({ + 'token': 'xoxa-123456789abcdef', + 'msg': 'test with ts' + }) + + with patch.object(slack, "fetch_url") as fetch_url_mock: + mock_response = Mock() + mock_response.read.return_value = '{"fake":"data"}' + fetch_url_mock.return_value = (mock_response, {"status": 200}) + with self.assertRaises(AnsibleExitJson): + 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") + + def test_edit_message(self): + set_module_args({ + 'token': 'xoxa-123456789abcdef', + 'msg': 'test2', + 'message_id': '12345' + }) + + with patch.object(slack, "fetch_url") as fetch_url_mock: + mock_response = Mock() + mock_response.read.return_value = '{"messages":[{"ts":"12345","msg":"test1"}]}' + fetch_url_mock.side_effect = [ + (mock_response, {"status": 200}), + (mock_response, {"status": 200}), + ] + with self.assertRaises(AnsibleExitJson): + 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") + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + self.assertEquals(call_data['ts'], "12345") + + def test_message_with_blocks(self): + """tests sending a message with blocks""" + set_module_args({ + 'token': 'XXXX/YYYY/ZZZZ', + 'msg': 'test', + 'blocks': [{ + 'type': 'section', + 'text': { + 'type': 'mrkdwn', + 'text': '*test*' + }, + 'accessory': { + 'type': 'image', + 'image_url': 'https://docs.ansible.com/favicon.ico', + 'alt_text': 'test' + } + }, { + 'type': 'section', + 'text': { + 'type': 'plain_text', + 'text': 'test', + 'emoji': True + } + }] + }) + + with patch.object(slack, "fetch_url") as fetch_url_mock: + fetch_url_mock.return_value = (None, {"status": 200}) + with self.assertRaises(AnsibleExitJson): + self.module.main() + + self.assertTrue(fetch_url_mock.call_count, 1) + call_data = json.loads(fetch_url_mock.call_args[1]['data']) + assert call_data['username'] == "Ansible" + assert call_data['blocks'][1]['text']['text'] == "test" + assert fetch_url_mock.call_args[1]['url'] == "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ" + + def test_message_with_invalid_color(self): + """tests sending invalid color value to module""" + set_module_args({ + 'token': 'XXXX/YYYY/ZZZZ', + 'msg': 'test', + 'color': 'aa', + }) + with self.assertRaises(AnsibleFailJson) as exec_info: + self.module.main() + + msg = "Color value specified should be either one of" \ + " ['normal', 'good', 'warning', 'danger'] or any valid" \ + " hex value with length 3 or 6." + assert exec_info.exception.args[0]['msg'] == msg + + +color_test = [ + ('#111111', True), + ('#00aabb', True), + ('#abc', True), + ('#gghhjj', False), + ('#ghj', False), + ('#a', False), + ('#aaaaaaaa', False), + ('', False), + ('aaaa', False), + ('$00aabb', False), + ('$00a', False), +] + + +@pytest.mark.parametrize("color_value, ret_status", color_test) +def test_is_valid_hex_color(color_value, ret_status): + generated_value = slack.is_valid_hex_color(color_value) + assert generated_value == ret_status diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_solaris_zone.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_solaris_zone.py new file mode 100644 index 000000000..20b550875 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_solaris_zone.py @@ -0,0 +1,116 @@ +# Copyright (c) 2020 Justin Bronn +# 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 +import platform + +import pytest +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.modules import ( + solaris_zone +) +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + set_module_args, +) + + +ZONEADM = "/usr/sbin/zoneadm" + + +def mocker_zone_set(mocker, rc=0, out="", err="", zone_exists=False, zone_status=None): + """ + Configure common mocker object for Solaris Zone tests + """ + exists = mocker.patch.object(solaris_zone.Zone, "exists") + exists.return_value = zone_exists + get_bin_path = mocker.patch.object(AnsibleModule, "get_bin_path") + get_bin_path.return_value = ZONEADM + run_command = mocker.patch.object(AnsibleModule, "run_command") + run_command.return_value = (rc, out, err) + platform_release = mocker.patch.object(platform, "release") + platform_release.return_value = "5.11" + platform_system = mocker.patch.object(platform, "system") + platform_system.return_value = "SunOS" + if zone_status is not None: + status = mocker.patch.object(solaris_zone.Zone, "status") + status.return_value = zone_status + + +@pytest.fixture +def mocked_zone_create(mocker): + mocker_zone_set(mocker) + + +@pytest.fixture +def mocked_zone_delete(mocker): + mocker_zone_set(mocker, zone_exists=True, zone_status="running") + + +def test_zone_create(mocked_zone_create, capfd): + """ + test zone creation + """ + set_module_args( + { + "name": "z1", + "state": "installed", + "path": "/zones/z1", + "_ansible_check_mode": False, + } + ) + with pytest.raises(SystemExit): + solaris_zone.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get("failed") + assert results["changed"] + + +def test_zone_delete(mocked_zone_delete, capfd): + """ + test zone deletion + """ + set_module_args( + { + "name": "z1", + "state": "absent", + "path": "/zones/z1", + "_ansible_check_mode": False, + } + ) + with pytest.raises(SystemExit): + solaris_zone.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert not results.get("failed") + assert results["changed"] + + +def test_zone_create_invalid_names(mocked_zone_create, capfd): + """ + test zone creation with invalid names + """ + # 1. Invalid character ('!'). + # 2. Zone name > 64 characters. + # 3. Zone name beginning with non-alphanumeric character. + for invalid_name in ('foo!bar', 'z' * 65, '_zone'): + set_module_args( + { + "name": invalid_name, + "state": "installed", + "path": "/zones/" + invalid_name, + "_ansible_check_mode": False, + } + ) + with pytest.raises(SystemExit): + solaris_zone.main() + + out, err = capfd.readouterr() + results = json.loads(out) + assert results.get("failed") diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ss_3par_cpg.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ss_3par_cpg.py new file mode 100644 index 000000000..8e935b8de --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ss_3par_cpg.py @@ -0,0 +1,248 @@ +# Copyright (c) 2018, Hewlett Packard Enterprise Development LP +# 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 mock +import sys +sys.modules['hpe3par_sdk'] = mock.Mock() +sys.modules['hpe3par_sdk.client'] = mock.Mock() +sys.modules['hpe3parclient'] = mock.Mock() +sys.modules['hpe3parclient.exceptions'] = mock.Mock() +from ansible_collections.community.general.plugins.modules import ss_3par_cpg +from ansible_collections.community.general.plugins.module_utils.storage.hpe3par import hpe3par + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.AnsibleModule') +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.create_cpg') +def test_module_args(mock_create_cpg, mock_module, mock_client): + """ + hpe3par CPG - test module arguments + """ + + PARAMS_FOR_PRESENT = { + 'storage_system_ip': '192.168.0.1', + 'storage_system_username': 'USER', + 'storage_system_password': 'PASS', + 'cpg_name': 'test_cpg', + 'domain': 'test_domain', + 'growth_increment': 32768, + 'growth_increment_unit': 'MiB', + 'growth_limit': 32768, + 'growth_limit_unit': 'MiB', + 'growth_warning': 32768, + 'growth_warning_unit': 'MiB', + 'raid_type': 'R6', + 'set_size': 8, + 'high_availability': 'MAG', + 'disk_type': 'FC', + 'state': 'present', + 'secure': False + } + mock_module.params = PARAMS_FOR_PRESENT + mock_module.return_value = mock_module + mock_client.HPE3ParClient.login.return_value = True + mock_create_cpg.return_value = (True, True, "Created CPG successfully.") + ss_3par_cpg.main() + mock_module.assert_called_with( + argument_spec=hpe3par.cpg_argument_spec(), + required_together=[['raid_type', 'set_size']]) + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.AnsibleModule') +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.create_cpg') +def test_main_exit_functionality_present_success_without_issue_attr_dict(mock_create_cpg, mock_module, mock_client): + """ + hpe3par flash cache - success check + """ + PARAMS_FOR_PRESENT = { + 'storage_system_ip': '192.168.0.1', + 'storage_system_name': '3PAR', + 'storage_system_username': 'USER', + 'storage_system_password': 'PASS', + 'cpg_name': 'test_cpg', + 'domain': 'test_domain', + 'growth_increment': 32768, + 'growth_increment_unit': 'MiB', + 'growth_limit': 32768, + 'growth_limit_unit': 'MiB', + 'growth_warning': 32768, + 'growth_warning_unit': 'MiB', + 'raid_type': 'R6', + 'set_size': 8, + 'high_availability': 'MAG', + 'disk_type': 'FC', + 'state': 'present', + 'secure': False + } + # This creates a instance of the AnsibleModule mock. + mock_module.params = PARAMS_FOR_PRESENT + mock_module.return_value = mock_module + instance = mock_module.return_value + mock_client.HPE3ParClient.login.return_value = True + mock_create_cpg.return_value = ( + True, True, "Created CPG successfully.") + ss_3par_cpg.main() + # AnsibleModule.exit_json should be called + instance.exit_json.assert_called_with( + changed=True, msg="Created CPG successfully.") + # AnsibleModule.fail_json should not be called + assert instance.fail_json.call_count == 0 + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.AnsibleModule') +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.delete_cpg') +def test_main_exit_functionality_absent_success_without_issue_attr_dict(mock_delete_cpg, mock_module, mock_client): + """ + hpe3par flash cache - success check + """ + PARAMS_FOR_DELETE = { + 'storage_system_ip': '192.168.0.1', + 'storage_system_name': '3PAR', + 'storage_system_username': 'USER', + 'storage_system_password': 'PASS', + 'cpg_name': 'test_cpg', + 'domain': None, + 'growth_increment': None, + 'growth_increment_unit': None, + 'growth_limit': None, + 'growth_limit_unit': None, + 'growth_warning': None, + 'growth_warning_unit': None, + 'raid_type': None, + 'set_size': None, + 'high_availability': None, + 'disk_type': None, + 'state': 'absent', + 'secure': False + } + # This creates a instance of the AnsibleModule mock. + mock_module.params = PARAMS_FOR_DELETE + mock_module.return_value = mock_module + instance = mock_module.return_value + mock_delete_cpg.return_value = ( + True, True, "Deleted CPG test_cpg successfully.") + mock_client.HPE3ParClient.login.return_value = True + ss_3par_cpg.main() + # AnsibleModule.exit_json should be called + instance.exit_json.assert_called_with( + changed=True, msg="Deleted CPG test_cpg successfully.") + # AnsibleModule.fail_json should not be called + assert instance.fail_json.call_count == 0 + + +def test_convert_to_binary_multiple(): + assert hpe3par.convert_to_binary_multiple(None) == -1 + assert hpe3par.convert_to_binary_multiple('-1.0 MiB') == -1 + assert hpe3par.convert_to_binary_multiple('-1.0GiB') == -1 + assert hpe3par.convert_to_binary_multiple('1.0 MiB') == 1 + assert hpe3par.convert_to_binary_multiple('1.5GiB') == 1.5 * 1024 + assert hpe3par.convert_to_binary_multiple('1.5 TiB') == 1.5 * 1024 * 1024 + assert hpe3par.convert_to_binary_multiple(' 1.5 TiB ') == 1.5 * 1024 * 1024 + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +def test_validate_set_size(mock_client): + mock_client.HPE3ParClient.RAID_MAP = {'R0': {'raid_value': 1, 'set_sizes': [1]}, + 'R1': {'raid_value': 2, 'set_sizes': [2, 3, 4]}, + 'R5': {'raid_value': 3, 'set_sizes': [3, 4, 5, 6, 7, 8, 9]}, + 'R6': {'raid_value': 4, 'set_sizes': [6, 8, 10, 12, 16]} + } + raid_type = 'R0' + set_size = 1 + assert ss_3par_cpg.validate_set_size(raid_type, set_size) + + set_size = 2 + assert not ss_3par_cpg.validate_set_size(raid_type, set_size) + + raid_type = None + assert not ss_3par_cpg.validate_set_size(raid_type, set_size) + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +def test_cpg_ldlayout_map(mock_client): + mock_client.HPE3ParClient.PORT = 1 + mock_client.HPE3ParClient.RAID_MAP = {'R0': {'raid_value': 1, 'set_sizes': [1]}, + 'R1': {'raid_value': 2, 'set_sizes': [2, 3, 4]}, + 'R5': {'raid_value': 3, 'set_sizes': [3, 4, 5, 6, 7, 8, 9]}, + 'R6': {'raid_value': 4, 'set_sizes': [6, 8, 10, 12, 16]} + } + ldlayout_dict = {'RAIDType': 'R6', 'HA': 'PORT'} + assert ss_3par_cpg.cpg_ldlayout_map(ldlayout_dict) == { + 'RAIDType': 4, 'HA': 1} + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +def test_create_cpg(mock_client): + ss_3par_cpg.validate_set_size = mock.Mock(return_value=True) + ss_3par_cpg.cpg_ldlayout_map = mock.Mock( + return_value={'RAIDType': 4, 'HA': 1}) + + mock_client.HPE3ParClient.login.return_value = True + mock_client.HPE3ParClient.cpgExists.return_value = False + mock_client.HPE3ParClient.FC = 1 + mock_client.HPE3ParClient.createCPG.return_value = True + + assert ss_3par_cpg.create_cpg(mock_client.HPE3ParClient, + 'test_cpg', + 'test_domain', + '32768 MiB', + '32768 MiB', + '32768 MiB', + 'R6', + 8, + 'MAG', + 'FC' + ) == (True, True, "Created CPG %s successfully." % 'test_cpg') + + mock_client.HPE3ParClient.cpgExists.return_value = True + assert ss_3par_cpg.create_cpg(mock_client.HPE3ParClient, + 'test_cpg', + 'test_domain', + '32768.0 MiB', + '32768.0 MiB', + '32768.0 MiB', + 'R6', + 8, + 'MAG', + 'FC' + ) == (True, False, 'CPG already present') + + ss_3par_cpg.validate_set_size = mock.Mock(return_value=False) + assert ss_3par_cpg.create_cpg(mock_client.HPE3ParClient, + 'test_cpg', + 'test_domain', + '32768.0 MiB', + '32768 MiB', + '32768.0 MiB', + 'R6', + 3, + 'MAG', + 'FC' + ) == (False, False, 'Set size 3 not part of RAID set R6') + + +@mock.patch('ansible_collections.community.general.plugins.modules.ss_3par_cpg.client') +def test_delete_cpg(mock_client): + mock_client.HPE3ParClient.login.return_value = True + mock_client.HPE3ParClient.cpgExists.return_value = True + mock_client.HPE3ParClient.FC = 1 + mock_client.HPE3ParClient.deleteCPG.return_value = True + + assert ss_3par_cpg.delete_cpg(mock_client.HPE3ParClient, + 'test_cpg' + ) == (True, True, "Deleted CPG %s successfully." % 'test_cpg') + + mock_client.HPE3ParClient.cpgExists.return_value = False + + assert ss_3par_cpg.delete_cpg(mock_client.HPE3ParClient, + 'test_cpg' + ) == (True, False, "CPG does not exist") + assert ss_3par_cpg.delete_cpg(mock_client.HPE3ParClient, + None + ) == (True, False, "CPG does not exist") diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_statsd.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_statsd.py new file mode 100644 index 000000000..7d458c5eb --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_statsd.py @@ -0,0 +1,101 @@ +# 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.plugins.modules import statsd +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 + + +class FakeStatsD(MagicMock): + + def incr(self, *args, **kwargs): + pass + + def gauge(self, *args, **kwargs): + pass + + def close(self, *args, **kwargs): + pass + + +class TestStatsDModule(ModuleTestCase): + + def setUp(self): + super(TestStatsDModule, self).setUp() + statsd.HAS_STATSD = True + self.module = statsd + + def tearDown(self): + super(TestStatsDModule, self).tearDown() + + def patch_udp_statsd_client(self, **kwargs): + return patch('ansible_collections.community.general.plugins.modules.statsd.udp_statsd_client', autospec=True, **kwargs) + + def patch_tcp_statsd_client(self, **kwargs): + return patch('ansible_collections.community.general.plugins.modules.statsd.tcp_statsd_client', autospec=True, **kwargs) + + def test_udp_without_parameters(self): + """Test udp without parameters""" + with self.patch_udp_statsd_client(side_effect=FakeStatsD) as fake_statsd: + with self.assertRaises(AnsibleFailJson) as result: + set_module_args({}) + self.module.main() + + def test_tcp_without_parameters(self): + """Test tcp without parameters""" + with self.patch_tcp_statsd_client(side_effect=FakeStatsD) as fake_statsd: + with self.assertRaises(AnsibleFailJson) as result: + set_module_args({}) + self.module.main() + + def test_udp_with_parameters(self): + """Test udp with parameters""" + with self.patch_udp_statsd_client(side_effect=FakeStatsD) as fake_statsd: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args({ + 'metric': 'my_counter', + 'metric_type': 'counter', + 'value': 1, + }) + self.module.main() + self.assertEqual(result.exception.args[0]['msg'], 'Sent counter my_counter -> 1 to StatsD') + self.assertEqual(result.exception.args[0]['changed'], True) + with self.patch_udp_statsd_client(side_effect=FakeStatsD) as fake_statsd: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args({ + 'metric': 'my_gauge', + 'metric_type': 'gauge', + 'value': 3, + }) + self.module.main() + self.assertEqual(result.exception.args[0]['msg'], 'Sent gauge my_gauge -> 3 (delta=False) to StatsD') + self.assertEqual(result.exception.args[0]['changed'], True) + + def test_tcp_with_parameters(self): + """Test tcp with parameters""" + with self.patch_tcp_statsd_client(side_effect=FakeStatsD) as fake_statsd: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args({ + 'protocol': 'tcp', + 'metric': 'my_counter', + 'metric_type': 'counter', + 'value': 1, + }) + self.module.main() + self.assertEqual(result.exception.args[0]['msg'], 'Sent counter my_counter -> 1 to StatsD') + self.assertEqual(result.exception.args[0]['changed'], True) + with self.patch_tcp_statsd_client(side_effect=FakeStatsD) as fake_statsd: + with self.assertRaises(AnsibleExitJson) as result: + set_module_args({ + 'protocol': 'tcp', + 'metric': 'my_gauge', + 'metric_type': 'gauge', + 'value': 3, + }) + self.module.main() + self.assertEqual(result.exception.args[0]['msg'], 'Sent gauge my_gauge -> 3 (delta=False) to StatsD') + self.assertEqual(result.exception.args[0]['changed'], True) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_sysupgrade.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_sysupgrade.py new file mode 100644 index 000000000..77d1f1cd0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_sysupgrade.py @@ -0,0 +1,69 @@ +# 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.module_utils import basic +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 +from ansible_collections.community.general.plugins.modules import sysupgrade + + +class TestSysupgradeModule(ModuleTestCase): + + def setUp(self): + super(TestSysupgradeModule, self).setUp() + self.module = sysupgrade + self.mock_get_bin_path = (patch('ansible.module_utils.basic.AnsibleModule.get_bin_path')) + self.get_bin_path = self.mock_get_bin_path.start() + + def tearDown(self): + super(TestSysupgradeModule, self).tearDown() + self.mock_get_bin_path.stop() + + def test_upgrade_success(self): + """ Upgrade was successful """ + + rc = 0 + stdout = """ + SHA256.sig 100% |*************************************| 2141 00:00 + Signature Verified + INSTALL.amd64 100% |************************************| 43512 00:00 + base67.tgz 100% |*************************************| 238 MB 02:16 + bsd 100% |*************************************| 18117 KB 00:24 + bsd.mp 100% |*************************************| 18195 KB 00:17 + bsd.rd 100% |*************************************| 10109 KB 00:14 + comp67.tgz 100% |*************************************| 74451 KB 00:53 + game67.tgz 100% |*************************************| 2745 KB 00:03 + man67.tgz 100% |*************************************| 7464 KB 00:04 + xbase67.tgz 100% |*************************************| 22912 KB 00:30 + xfont67.tgz 100% |*************************************| 39342 KB 00:28 + xserv67.tgz 100% |*************************************| 16767 KB 00:24 + xshare67.tgz 100% |*************************************| 4499 KB 00:06 + Verifying sets. + Fetching updated firmware. + Will upgrade on next reboot + """ + stderr = "" + + with patch.object(basic.AnsibleModule, "run_command") as run_command: + run_command.return_value = (rc, stdout, stderr) + with self.assertRaises(AnsibleExitJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['changed']) + + def test_upgrade_failed(self): + """ Upgrade failed """ + + rc = 1 + stdout = "" + stderr = "sysupgrade: need root privileges" + + with patch.object(basic.AnsibleModule, "run_command") as run_command_mock: + run_command_mock.return_value = (rc, stdout, stderr) + with self.assertRaises(AnsibleFailJson) as result: + self.module.main() + self.assertTrue(result.exception.args[0]['failed']) + self.assertIn('need root', result.exception.args[0]['msg']) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_terraform.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_terraform.py new file mode 100644 index 000000000..f6a0593fd --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_terraform.py @@ -0,0 +1,23 @@ +# Copyright (c) 2019, 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 + +import pytest + +from ansible_collections.community.general.plugins.modules import terraform +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args + + +def test_terraform_without_argument(capfd): + set_module_args({}) + with pytest.raises(SystemExit) as results: + terraform.main() + + out, err = capfd.readouterr() + assert not err + assert json.loads(out)['failed'] + assert 'project_path' in json.loads(out)['msg'] diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ufw.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ufw.py new file mode 100644 index 000000000..da8f0f2c8 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ufw.py @@ -0,0 +1,477 @@ +# 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 +import ansible_collections.community.general.plugins.modules.ufw as module + +import json + + +# mock ufw messages + +ufw_version_35 = """ufw 0.35\nCopyright 2008-2015 Canonical Ltd.\n""" + +ufw_verbose_header = """Status: active +Logging: on (low) +Default: deny (incoming), allow (outgoing), deny (routed) +New profiles: skip + +To Action From +-- ------ ----""" + + +ufw_status_verbose_with_port_7000 = ufw_verbose_header + """ +7000/tcp ALLOW IN Anywhere +7000/tcp (v6) ALLOW IN Anywhere (v6) +""" + +user_rules_with_port_7000 = """### tuple ### allow tcp 7000 0.0.0.0/0 any 0.0.0.0/0 in +### tuple ### allow tcp 7000 ::/0 any ::/0 in +""" + +user_rules_with_ipv6 = """### tuple ### allow udp 5353 0.0.0.0/0 any 224.0.0.251 in +### tuple ### allow udp 5353 ::/0 any ff02::fb in +""" + +ufw_status_verbose_with_ipv6 = ufw_verbose_header + """ +5353/udp ALLOW IN 224.0.0.251 +5353/udp ALLOW IN ff02::fb +""" + +ufw_status_verbose_nothing = ufw_verbose_header + +skippg_adding_existing_rules = "Skipping adding existing rule\nSkipping adding existing rule (v6)\n" + +grep_config_cli = "grep -h '^### tuple' /lib/ufw/user.rules /lib/ufw/user6.rules /etc/ufw/user.rules /etc/ufw/user6.rules " +grep_config_cli += "/var/lib/ufw/user.rules /var/lib/ufw/user6.rules" + +dry_mode_cmd_with_port_700 = { + "ufw status verbose": ufw_status_verbose_with_port_7000, + "ufw --version": ufw_version_35, + "ufw --dry-run allow from any to any port 7000 proto tcp": skippg_adding_existing_rules, + "ufw --dry-run insert 1 allow from any to any port 7000 proto tcp": skippg_adding_existing_rules, + "ufw --dry-run delete allow from any to any port 7000 proto tcp": "", + "ufw --dry-run delete allow from any to any port 7001 proto tcp": user_rules_with_port_7000, + "ufw --dry-run route allow in on foo out on bar from 1.1.1.1 port 7000 to 8.8.8.8 port 7001 proto tcp": "", + "ufw --dry-run allow in on foo from any to any port 7003 proto tcp": "", + "ufw --dry-run allow in on foo from 1.1.1.1 port 7002 to 8.8.8.8 port 7003 proto tcp": "", + "ufw --dry-run allow out on foo from any to any port 7004 proto tcp": "", + "ufw --dry-run allow out on foo from 1.1.1.1 port 7003 to 8.8.8.8 port 7004 proto tcp": "", + grep_config_cli: user_rules_with_port_7000 +} + +# setup configuration : +# ufw reset +# ufw enable +# ufw allow proto udp to any port 5353 from 224.0.0.251 +# ufw allow proto udp to any port 5353 from ff02::fb +dry_mode_cmd_with_ipv6 = { + "ufw status verbose": ufw_status_verbose_with_ipv6, + "ufw --version": ufw_version_35, + # CONTENT of the command sudo ufw --dry-run delete allow in from ff02::fb port 5353 proto udp | grep -E "^### tupple" + "ufw --dry-run delete allow from ff02::fb to any port 5353 proto udp": "### tuple ### allow udp any ::/0 5353 ff02::fb in", + grep_config_cli: user_rules_with_ipv6, + "ufw --dry-run allow from ff02::fb to any port 5353 proto udp": skippg_adding_existing_rules, + "ufw --dry-run allow from 224.0.0.252 to any port 5353 proto udp": """### tuple ### allow udp 5353 0.0.0.0/0 any 224.0.0.251 in +### tuple ### allow udp 5353 0.0.0.0/0 any 224.0.0.252 in +""", + "ufw --dry-run allow from 10.0.0.0/24 to any port 1577 proto udp": "### tuple ### allow udp 1577 0.0.0.0/0 any 10.0.0.0/24 in" +} + +dry_mode_cmd_nothing = { + "ufw status verbose": ufw_status_verbose_nothing, + "ufw --version": ufw_version_35, + grep_config_cli: "", + "ufw --dry-run allow from any to :: port 23": "### tuple ### allow any 23 :: any ::/0 in" +} + + +def do_nothing_func_nothing(*args, **kwarg): + return 0, dry_mode_cmd_nothing[args[0]], "" + + +def do_nothing_func_ipv6(*args, **kwarg): + return 0, dry_mode_cmd_with_ipv6[args[0]], "" + + +def do_nothing_func_port_7000(*args, **kwarg): + return 0, dry_mode_cmd_with_port_700[args[0]], "" + + +def set_module_args(args): + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + """prepare arguments so that they will be picked up during module creation""" + 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""" + return arg + + +class TestUFW(unittest.TestCase): + + def setUp(self): + self.mock_module_helper = 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) + + def test_filter_line_that_contains_ipv4(self): + reg = module.compile_ipv4_regexp() + + self.assertTrue(reg.search("### tuple ### allow udp 5353 ::/0 any ff02::fb in") is None) + self.assertTrue(reg.search("### tuple ### allow udp 5353 0.0.0.0/0 any 224.0.0.251 in") is not None) + + self.assertTrue(reg.match("ff02::fb") is None) + self.assertTrue(reg.match("224.0.0.251") is not None) + self.assertTrue(reg.match("10.0.0.0/8") is not None) + self.assertTrue(reg.match("somethingElse") is None) + self.assertTrue(reg.match("::") is None) + self.assertTrue(reg.match("any") is None) + + def test_filter_line_that_contains_ipv6(self): + reg = module.compile_ipv6_regexp() + self.assertTrue(reg.search("### tuple ### allow udp 5353 ::/0 any ff02::fb in") is not None) + self.assertTrue(reg.search("### tuple ### allow udp 5353 0.0.0.0/0 any 224.0.0.251 in") is None) + self.assertTrue(reg.search("### tuple ### allow any 23 :: any ::/0 in") is not None) + self.assertTrue(reg.match("ff02::fb") is not None) + self.assertTrue(reg.match("224.0.0.251") is None) + self.assertTrue(reg.match("::") is not None) + + def test_check_mode_add_rules(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7000', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertFalse(result.exception.args[0]['changed']) + + def test_check_mode_add_insert_rules(self): + set_module_args({ + 'insert': '1', + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7000', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertFalse(result.exception.args[0]['changed']) + + def test_check_mode_add_detailed_route(self): + set_module_args({ + 'rule': 'allow', + 'route': 'yes', + 'interface_in': 'foo', + 'interface_out': 'bar', + 'proto': 'tcp', + 'from_ip': '1.1.1.1', + 'to_ip': '8.8.8.8', + 'from_port': '7000', + 'to_port': '7001', + '_ansible_check_mode': True + }) + + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_add_ambiguous_route(self): + set_module_args({ + 'rule': 'allow', + 'route': 'yes', + 'interface_in': 'foo', + 'interface_out': 'bar', + 'direction': 'in', + 'interface': 'baz', + '_ansible_check_mode': True + }) + + with self.assertRaises(AnsibleFailJson) as result: + self.__getResult(do_nothing_func_port_7000) + + exc = result.exception.args[0] + self.assertTrue(exc['failed']) + self.assertIn('mutually exclusive', exc['msg']) + + def test_check_mode_add_interface_in(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7003', + 'interface_in': 'foo', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_add_interface_out(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7004', + 'interface_out': 'foo', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_add_non_route_interface_both(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7004', + 'interface_in': 'foo', + 'interface_out': 'bar', + '_ansible_check_mode': True + }) + + with self.assertRaises(AnsibleFailJson) as result: + self.__getResult(do_nothing_func_port_7000) + + exc = result.exception.args[0] + self.assertTrue(exc['failed']) + self.assertIn('combine', exc['msg']) + + def test_check_mode_add_direction_in(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7003', + 'direction': 'in', + 'interface': 'foo', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_add_direction_in_with_ip(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'from_ip': '1.1.1.1', + 'from_port': '7002', + 'to_ip': '8.8.8.8', + 'to_port': '7003', + 'direction': 'in', + 'interface': 'foo', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_add_direction_out(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7004', + 'direction': 'out', + 'interface': 'foo', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_add_direction_out_with_ip(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'from_ip': '1.1.1.1', + 'from_port': '7003', + 'to_ip': '8.8.8.8', + 'to_port': '7004', + 'direction': 'out', + 'interface': 'foo', + '_ansible_check_mode': True + }) + result = self.__getResult(do_nothing_func_port_7000) + self.assertTrue(result.exception.args[0]['changed']) + + def test_check_mode_delete_existing_rules(self): + + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7000', + 'delete': 'yes', + '_ansible_check_mode': True, + }) + + self.assertTrue(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_check_mode_delete_existing_insert_rules(self): + + set_module_args({ + 'insert': '1', + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7000', + 'delete': 'yes', + '_ansible_check_mode': True, + }) + + self.assertTrue(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_check_mode_delete_not_existing_rules(self): + + set_module_args({ + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7001', + 'delete': 'yes', + '_ansible_check_mode': True, + }) + + self.assertFalse(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_check_mode_delete_not_existing_insert_rules(self): + + set_module_args({ + 'insert': '1', + 'rule': 'allow', + 'proto': 'tcp', + 'port': '7001', + 'delete': 'yes', + '_ansible_check_mode': True, + }) + + self.assertFalse(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_enable_mode(self): + set_module_args({ + 'state': 'enabled', + '_ansible_check_mode': True + }) + + self.assertFalse(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_disable_mode(self): + set_module_args({ + 'state': 'disabled', + '_ansible_check_mode': True + }) + + self.assertTrue(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_logging_off(self): + set_module_args({ + 'logging': 'off', + '_ansible_check_mode': True + }) + + self.assertTrue(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_logging_on(self): + set_module_args({ + 'logging': 'on', + '_ansible_check_mode': True + }) + + self.assertFalse(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_default_changed(self): + set_module_args({ + 'default': 'allow', + "direction": "incoming", + '_ansible_check_mode': True + }) + self.assertTrue(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_default_not_changed(self): + set_module_args({ + 'default': 'deny', + "direction": "incoming", + '_ansible_check_mode': True + }) + self.assertFalse(self.__getResult(do_nothing_func_port_7000).exception.args[0]['changed']) + + def test_ipv6_remove(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'udp', + 'port': '5353', + 'from': 'ff02::fb', + 'delete': 'yes', + '_ansible_check_mode': True, + }) + self.assertTrue(self.__getResult(do_nothing_func_ipv6).exception.args[0]['changed']) + + def test_ipv6_add_existing(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'udp', + 'port': '5353', + 'from': 'ff02::fb', + '_ansible_check_mode': True, + }) + self.assertFalse(self.__getResult(do_nothing_func_ipv6).exception.args[0]['changed']) + + def test_add_not_existing_ipv4_submask(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'udp', + 'port': '1577', + 'from': '10.0.0.0/24', + '_ansible_check_mode': True, + }) + self.assertTrue(self.__getResult(do_nothing_func_ipv6).exception.args[0]['changed']) + + def test_ipv4_add_with_existing_ipv6(self): + set_module_args({ + 'rule': 'allow', + 'proto': 'udp', + 'port': '5353', + 'from': '224.0.0.252', + '_ansible_check_mode': True, + }) + self.assertTrue(self.__getResult(do_nothing_func_ipv6).exception.args[0]['changed']) + + def test_ipv6_add_from_nothing(self): + set_module_args({ + 'rule': 'allow', + 'port': '23', + 'to': '::', + '_ansible_check_mode': True, + }) + result = self.__getResult(do_nothing_func_nothing).exception.args[0] + print(result) + self.assertTrue(result['changed']) + + def __getResult(self, cmd_fun): + with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command: + mock_run_command.side_effect = cmd_fun + with self.assertRaises(AnsibleExitJson) as result: + module.main() + return result diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_command.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_command.py new file mode 100644 index 000000000..332b976f7 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_command.py @@ -0,0 +1,911 @@ +# -*- 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 shutil +import uuid +import tarfile +import tempfile +import os + +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible.module_utils import basic +import ansible_collections.community.general.plugins.modules.wdc_redfish_command as module +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args, exit_json, fail_json + +MOCK_SUCCESSFUL_HTTP_EMPTY_RESPONSE = { + "ret": True, + "data": { + } +} + +MOCK_GET_ENCLOSURE_RESPONSE_SINGLE_TENANT = { + "ret": True, + "data": { + "SerialNumber": "12345" + } +} + +MOCK_GET_ENCLOSURE_RESPONSE_MULTI_TENANT = { + "ret": True, + "data": { + "SerialNumber": "12345-A" + } +} + +MOCK_URL_ERROR = { + "ret": False, + "msg": "This is a mock URL error", + "status": 500 +} + +MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE = { + "ret": True, + "data": { + "UpdateService": { + "@odata.id": "/UpdateService" + }, + "Chassis": { + "@odata.id": "/Chassis" + } + } +} + +MOCK_SUCCESSFUL_RESPONSE_CHASSIS = { + "ret": True, + "data": { + "Members": [ + { + "@odata.id": "/redfish/v1/Chassis/Enclosure" + } + ] + } +} + +MOCK_SUCCESSFUL_RESPONSE_CHASSIS_ENCLOSURE = { + "ret": True, + "data": { + "Id": "Enclosure", + "IndicatorLED": "Off", + "Actions": { + "Oem": { + "WDC": { + "#Chassis.Locate": { + "target": "/Chassis.Locate" + }, + "#Chassis.PowerMode": { + "target": "/redfish/v1/Chassis/Enclosure/Actions/Chassis.PowerMode", + } + } + } + }, + "Oem": { + "WDC": { + "PowerMode": "Normal" + } + } + } +} + +MOCK_SUCCESSFUL_RESPONSE_WITH_SIMPLE_UPDATE_AND_FW_ACTIVATE = { + "ret": True, + "data": { + "Actions": { + "#UpdateService.SimpleUpdate": { + "target": "mocked value" + }, + "Oem": { + "WDC": { + "#UpdateService.FWActivate": { + "title": "Activate the downloaded firmware.", + "target": "/redfish/v1/UpdateService/Actions/UpdateService.FWActivate" + } + } + } + } + } +} + +MOCK_SUCCESSFUL_RESPONSE_WITH_ACTIONS = { + "ret": True, + "data": { + "Actions": {} + } +} + +MOCK_GET_IOM_A_MULTI_TENANT = { + "ret": True, + "data": { + "Id": "IOModuleAFRU" + } +} + +MOCK_GET_IOM_B_MULTI_TENANAT = { + "ret": True, + "data": { + "error": { + "message": "IOM Module B cannot be read" + } + } +} + + +MOCK_READY_FOR_FW_UPDATE = { + "ret": True, + "entries": { + "Description": "Ready for FW update", + "StatusCode": 0 + } +} + +MOCK_FW_UPDATE_IN_PROGRESS = { + "ret": True, + "entries": { + "Description": "FW update in progress", + "StatusCode": 1 + } +} + +MOCK_WAITING_FOR_ACTIVATION = { + "ret": True, + "entries": { + "Description": "FW update completed. Waiting for activation.", + "StatusCode": 2 + } +} + +MOCK_SIMPLE_UPDATE_STATUS_LIST = [ + MOCK_READY_FOR_FW_UPDATE, + MOCK_FW_UPDATE_IN_PROGRESS, + MOCK_WAITING_FOR_ACTIVATION +] + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + return arg + + +def get_exception_message(ansible_exit_json): + """From an AnsibleExitJson exception, get the message string.""" + return ansible_exit_json.exception.args[0]["msg"] + + +def is_changed(ansible_exit_json): + """From an AnsibleExitJson exception, return the value of the changed flag""" + return ansible_exit_json.exception.args[0]["changed"] + + +def mock_simple_update(*args, **kwargs): + return { + "ret": True + } + + +def mocked_url_response(*args, **kwargs): + """Mock to just return a generic string.""" + return "/mockedUrl" + + +def mock_update_url(*args, **kwargs): + """Mock of the update url""" + return "/UpdateService" + + +def mock_fw_activate_url(*args, **kwargs): + """Mock of the FW Activate URL""" + return "/UpdateService.FWActivate" + + +def empty_return(*args, **kwargs): + """Mock to just return an empty successful return.""" + return {"ret": True} + + +def mock_get_simple_update_status_ready_for_fw_update(*args, **kwargs): + """Mock to return simple update status Ready for FW update""" + return MOCK_READY_FOR_FW_UPDATE + + +def mock_get_request_enclosure_single_tenant(*args, **kwargs): + """Mock for get_request for single-tenant enclosure.""" + if args[1].endswith("/redfish/v1") or args[1].endswith("/redfish/v1/"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE + elif args[1].endswith("/mockedUrl"): + return MOCK_SUCCESSFUL_HTTP_EMPTY_RESPONSE + elif args[1].endswith("Chassis/Enclosure"): + return MOCK_GET_ENCLOSURE_RESPONSE_SINGLE_TENANT + elif args[1].endswith("/UpdateService"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_SIMPLE_UPDATE_AND_FW_ACTIVATE + else: + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_get_request_enclosure_multi_tenant(*args, **kwargs): + """Mock for get_request with multi-tenant enclosure.""" + if args[1].endswith("/redfish/v1") or args[1].endswith("/redfish/v1/"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE + elif args[1].endswith("/mockedUrl"): + return MOCK_SUCCESSFUL_HTTP_EMPTY_RESPONSE + elif args[1].endswith("Chassis/Enclosure"): + return MOCK_GET_ENCLOSURE_RESPONSE_MULTI_TENANT + elif args[1].endswith("/UpdateService"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_SIMPLE_UPDATE_AND_FW_ACTIVATE + elif args[1].endswith("/IOModuleAFRU"): + return MOCK_GET_IOM_A_MULTI_TENANT + elif args[1].endswith("/IOModuleBFRU"): + return MOCK_GET_IOM_B_MULTI_TENANAT + else: + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_get_request(*args, **kwargs): + """Mock for get_request for simple resource tests.""" + if args[1].endswith("/redfish/v1") or args[1].endswith("/redfish/v1/"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE + elif args[1].endswith("/Chassis"): + return MOCK_SUCCESSFUL_RESPONSE_CHASSIS + elif args[1].endswith("Chassis/Enclosure"): + return MOCK_SUCCESSFUL_RESPONSE_CHASSIS_ENCLOSURE + else: + raise RuntimeError("Illegal call to get_request in test: " + args[1]) + + +def mock_post_request(*args, **kwargs): + """Mock post_request with successful response.""" + valid_endpoints = [ + "/UpdateService.FWActivate", + "/Chassis.Locate", + "/Chassis.PowerMode", + ] + for endpoint in valid_endpoints: + if args[1].endswith(endpoint): + return { + "ret": True, + "data": ACTION_WAS_SUCCESSFUL_MESSAGE + } + raise RuntimeError("Illegal POST call to: " + args[1]) + + +def mock_get_firmware_inventory_version_1_2_3(*args, **kwargs): + return { + "ret": True, + "entries": [ + { + "Id": "IOModuleA_OOBM", + "Version": "1.2.3" + }, + { + "Id": "IOModuleB_OOBM", + "Version": "1.2.3" + } + ] + } + + +ERROR_MESSAGE_UNABLE_TO_EXTRACT_BUNDLE_VERSION = "Unable to extract bundle version or multi-tenant status from update image tarfile" +ACTION_WAS_SUCCESSFUL_MESSAGE = "Action was successful" + + +class TestWdcRedfishCommand(unittest.TestCase): + + def setUp(self): + self.mock_module_helper = 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.tempdir = tempfile.mkdtemp() + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + module.main() + + def test_module_fail_when_unknown_category(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'unknown', + 'command': 'FWActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': [], + }) + module.main() + + def test_module_fail_when_unknown_command(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'Update', + 'command': 'unknown', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': [], + }) + module.main() + + def test_module_chassis_power_mode_low(self): + """Test setting chassis power mode to low (happy path).""" + module_args = { + 'category': 'Chassis', + 'command': 'PowerModeLow', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_id': 'Enclosure', + 'baseuri': 'example.com' + } + set_module_args(module_args) + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_request=mock_get_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, + get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_module_chassis_power_mode_normal_when_already_normal(self): + """Test setting chassis power mode to normal when it already is. Verify we get changed=False.""" + module_args = { + 'category': 'Chassis', + 'command': 'PowerModeNormal', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_id': 'Enclosure', + 'baseuri': 'example.com' + } + set_module_args(module_args) + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_request=mock_get_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, + get_exception_message(ansible_exit_json)) + self.assertFalse(is_changed(ansible_exit_json)) + + def test_module_chassis_power_mode_invalid_command(self): + """Test that we get an error when issuing an invalid PowerMode command.""" + module_args = { + 'category': 'Chassis', + 'command': 'PowerModeExtraHigh', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_id': 'Enclosure', + 'baseuri': 'example.com' + } + set_module_args(module_args) + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_request=mock_get_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + module.main() + expected_error_message = "Invalid Command 'PowerModeExtraHigh'" + self.assertIn(expected_error_message, + get_exception_message(ansible_fail_json)) + + def test_module_enclosure_led_indicator_on(self): + """Test turning on a valid LED indicator (in this case we use the Enclosure resource).""" + module_args = { + 'category': 'Chassis', + 'command': 'IndicatorLedOn', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + "resource_id": "Enclosure", + "baseuri": "example.com" + } + set_module_args(module_args) + + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_request=mock_get_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, + get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_module_invalid_resource_led_indicator_on(self): + """Test turning LED on for an invalid resource id.""" + module_args = { + 'category': 'Chassis', + 'command': 'IndicatorLedOn', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + "resource_id": "Disk99", + "baseuri": "example.com" + } + set_module_args(module_args) + + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_request=mock_get_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleFailJson) as ansible_fail_json: + module.main() + expected_error_message = "Chassis resource Disk99 not found" + self.assertEqual(expected_error_message, + get_exception_message(ansible_fail_json)) + + def test_module_enclosure_led_off_already_off(self): + """Test turning LED indicator off when it's already off. Confirm changed is False and no POST occurs.""" + module_args = { + 'category': 'Chassis', + 'command': 'IndicatorLedOff', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + "resource_id": "Enclosure", + "baseuri": "example.com" + } + set_module_args(module_args) + + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_request=mock_get_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, + get_exception_message(ansible_exit_json)) + self.assertFalse(is_changed(ansible_exit_json)) + + def test_module_fw_activate_first_iom_unavailable(self): + """Test that if the first IOM is not available, the 2nd one is used.""" + ioms = [ + "bad.example.com", + "good.example.com" + ] + module_args = { + 'category': 'Update', + 'command': 'FWActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ioms + } + set_module_args(module_args) + + def mock_get_request(*args, **kwargs): + """Mock for get_request that will fail on the 'bad' IOM.""" + if "bad.example.com" in args[1]: + return MOCK_URL_ERROR + else: + return mock_get_request_enclosure_single_tenant(*args, **kwargs) + + with patch.multiple(module.WdcRedfishUtils, + _firmware_activate_uri=mock_fw_activate_url, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as cm: + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, + get_exception_message(cm)) + + def test_module_fw_activate_pass(self): + """Test the FW Activate command in a passing scenario.""" + # Run the same test twice -- once specifying ioms, and once specifying baseuri. + # Both should work the same way. + uri_specifiers = [ + { + "ioms": ["example1.example.com"] + }, + { + "baseuri": "example1.example.com" + } + ] + for uri_specifier in uri_specifiers: + module_args = { + 'category': 'Update', + 'command': 'FWActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + } + module_args.update(uri_specifier) + set_module_args(module_args) + + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + _firmware_activate_uri=mock_fw_activate_url, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_single_tenant, + post_request=mock_post_request): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, + get_exception_message(ansible_exit_json)) + self.assertTrue(is_changed(ansible_exit_json)) + + def test_module_fw_activate_service_does_not_support_fw_activate(self): + """Test FW Activate when it is not supported.""" + expected_error_message = "Service does not support FWActivate" + set_module_args({ + 'category': 'Update', + 'command': 'FWActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"] + }) + + def mock_update_uri_response(*args, **kwargs): + return { + "ret": True, + "data": {} # No Actions + } + + with patch.multiple(module.WdcRedfishUtils, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_update_uri_response): + with self.assertRaises(AnsibleFailJson) as cm: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(cm)) + + def test_module_update_and_activate_image_uri_not_http(self): + """Test Update and Activate when URI is not http(s)""" + expected_error_message = "Bundle URI must be HTTP or HTTPS" + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "ftp://example.com/image" + }) + with patch.multiple(module.WdcRedfishUtils, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return): + with self.assertRaises(AnsibleFailJson) as cm: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(cm)) + + def test_module_update_and_activate_target_not_ready_for_fw_update(self): + """Test Update and Activate when target is not in the correct state.""" + mock_status_code = 999 + mock_status_description = "mock status description" + expected_error_message = "Target is not ready for FW update. Current status: {0} ({1})".format( + mock_status_code, + mock_status_description + ) + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image" + }) + with patch.object(module.WdcRedfishUtils, "get_simple_update_status") as mock_get_simple_update_status: + mock_get_simple_update_status.return_value = { + "ret": True, + "entries": { + "StatusCode": mock_status_code, + "Description": mock_status_description + } + } + + with patch.multiple(module.WdcRedfishUtils, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return): + with self.assertRaises(AnsibleFailJson) as cm: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(cm)) + + def test_module_update_and_activate_bundle_not_a_tarfile(self): + """Test Update and Activate when bundle is not a tarfile""" + mock_filename = os.path.abspath(__file__) + expected_error_message = ERROR_MESSAGE_UNABLE_TO_EXTRACT_BUNDLE_VERSION + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = mock_filename + with patch.multiple(module.WdcRedfishUtils, + get_simple_update_status=mock_get_simple_update_status_ready_for_fw_update, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return): + with self.assertRaises(AnsibleFailJson) as cm: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(cm)) + + def test_module_update_and_activate_bundle_contains_no_firmware_version(self): + """Test Update and Activate when bundle contains no firmware version""" + expected_error_message = ERROR_MESSAGE_UNABLE_TO_EXTRACT_BUNDLE_VERSION + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = "empty_tarfile{0}.tar".format(uuid.uuid4()) + empty_tarfile = tarfile.open(os.path.join(self.tempdir, tar_name), "w") + empty_tarfile.close() + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple(module.WdcRedfishUtils, + get_simple_update_status=mock_get_simple_update_status_ready_for_fw_update, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return): + with self.assertRaises(AnsibleFailJson) as cm: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(cm)) + + def test_module_update_and_activate_version_already_installed(self): + """Test Update and Activate when the bundle version is already installed""" + mock_firmware_version = "1.2.3" + expected_error_message = ACTION_WAS_SUCCESSFUL_MESSAGE + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = self.generate_temp_bundlefile(mock_firmware_version=mock_firmware_version, + is_multi_tenant=False) + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple(module.WdcRedfishUtils, + get_firmware_inventory=mock_get_firmware_inventory_version_1_2_3, + get_simple_update_status=mock_get_simple_update_status_ready_for_fw_update, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_single_tenant): + with self.assertRaises(AnsibleExitJson) as result: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(result)) + self.assertFalse(is_changed(result)) + + def test_module_update_and_activate_version_already_installed_multi_tenant(self): + """Test Update and Activate on multi-tenant when version is already installed""" + mock_firmware_version = "1.2.3" + expected_error_message = ACTION_WAS_SUCCESSFUL_MESSAGE + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = self.generate_temp_bundlefile(mock_firmware_version=mock_firmware_version, + is_multi_tenant=True) + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple(module.WdcRedfishUtils, + get_firmware_inventory=mock_get_firmware_inventory_version_1_2_3, + get_simple_update_status=mock_get_simple_update_status_ready_for_fw_update, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_multi_tenant): + with self.assertRaises(AnsibleExitJson) as result: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(result)) + self.assertFalse(is_changed(result)) + + def test_module_update_and_activate_pass(self): + """Test Update and Activate (happy path)""" + mock_firmware_version = "1.2.2" + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = self.generate_temp_bundlefile(mock_firmware_version=mock_firmware_version, + is_multi_tenant=False) + + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils", + get_firmware_inventory=mock_get_firmware_inventory_version_1_2_3, + simple_update=mock_simple_update, + _simple_update_status_uri=mocked_url_response, + # _find_updateservice_resource=empty_return, + # _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_single_tenant, + post_request=mock_post_request): + + with patch("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils.get_simple_update_status" + ) as mock_get_simple_update_status: + mock_get_simple_update_status.side_effect = MOCK_SIMPLE_UPDATE_STATUS_LIST + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertTrue(is_changed(ansible_exit_json)) + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, get_exception_message(ansible_exit_json)) + + def test_module_update_and_activate_pass_multi_tenant(self): + """Test Update and Activate with multi-tenant (happy path)""" + mock_firmware_version = "1.2.2" + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = self.generate_temp_bundlefile(mock_firmware_version=mock_firmware_version, + is_multi_tenant=True) + + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple(module.WdcRedfishUtils, + get_firmware_inventory=mock_get_firmware_inventory_version_1_2_3, + simple_update=mock_simple_update, + _simple_update_status_uri=mocked_url_response, + # _find_updateservice_resource=empty_return, + # _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_multi_tenant, + post_request=mock_post_request): + with patch("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils.get_simple_update_status" + ) as mock_get_simple_update_status: + mock_get_simple_update_status.side_effect = MOCK_SIMPLE_UPDATE_STATUS_LIST + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + self.assertTrue(is_changed(ansible_exit_json)) + self.assertEqual(ACTION_WAS_SUCCESSFUL_MESSAGE, get_exception_message(ansible_exit_json)) + + def test_module_fw_update_multi_tenant_firmware_single_tenant_enclosure(self): + """Test Update and Activate using multi-tenant bundle on single-tenant enclosure""" + mock_firmware_version = "1.1.1" + expected_error_message = "Enclosure multi-tenant is False but bundle multi-tenant is True" + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = self.generate_temp_bundlefile(mock_firmware_version=mock_firmware_version, + is_multi_tenant=True) + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple(module.WdcRedfishUtils, + get_firmware_inventory=mock_get_firmware_inventory_version_1_2_3(), + get_simple_update_status=mock_get_simple_update_status_ready_for_fw_update, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_single_tenant): + with self.assertRaises(AnsibleFailJson) as result: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(result)) + + def test_module_fw_update_single_tentant_firmware_multi_tenant_enclosure(self): + """Test Update and Activate using singe-tenant bundle on multi-tenant enclosure""" + mock_firmware_version = "1.1.1" + expected_error_message = "Enclosure multi-tenant is True but bundle multi-tenant is False" + set_module_args({ + 'category': 'Update', + 'command': 'UpdateAndActivate', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + 'update_image_uri': "http://example.com/image", + "update_creds": { + "username": "image_user", + "password": "image_password" + } + }) + + tar_name = self.generate_temp_bundlefile(mock_firmware_version=mock_firmware_version, + is_multi_tenant=False) + with patch('ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.fetch_file') as mock_fetch_file: + mock_fetch_file.return_value = os.path.join(self.tempdir, tar_name) + with patch.multiple(module.WdcRedfishUtils, + get_firmware_inventory=mock_get_firmware_inventory_version_1_2_3(), + get_simple_update_status=mock_get_simple_update_status_ready_for_fw_update, + _firmware_activate_uri=mocked_url_response, + _update_uri=mock_update_url, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_get_request_enclosure_multi_tenant): + with self.assertRaises(AnsibleFailJson) as result: + module.main() + self.assertEqual(expected_error_message, + get_exception_message(result)) + + def generate_temp_bundlefile(self, + mock_firmware_version, + is_multi_tenant): + """Generate a temporary fake bundle file. + + :param str mock_firmware_version: The simulated firmware version for the bundle. + :param bool is_multi_tenant: Is the simulated bundle multi-tenant? + + This can be used for a mock FW update. + """ + tar_name = "tarfile{0}.tar".format(uuid.uuid4()) + + bundle_tarfile = tarfile.open(os.path.join(self.tempdir, tar_name), "w") + package_filename = "oobm-{0}.pkg".format(mock_firmware_version) + package_filename_path = os.path.join(self.tempdir, package_filename) + package_file = open(package_filename_path, "w") + package_file.close() + bundle_tarfile.add(os.path.join(self.tempdir, package_filename), arcname=package_filename) + bin_filename = "firmware.bin" + bin_filename_path = os.path.join(self.tempdir, bin_filename) + bin_file = open(bin_filename_path, "wb") + byte_to_write = b'\x80' if is_multi_tenant else b'\xFF' + bin_file.write(byte_to_write * 12) + bin_file.close() + for filename in [package_filename, bin_filename]: + bundle_tarfile.add(os.path.join(self.tempdir, filename), arcname=filename) + bundle_tarfile.close() + return tar_name diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_info.py new file mode 100644 index 000000000..e1dfb4a27 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_wdc_redfish_info.py @@ -0,0 +1,216 @@ +# -*- 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 + +from ansible_collections.community.general.tests.unit.compat.mock import patch +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible.module_utils import basic +import ansible_collections.community.general.plugins.modules.wdc_redfish_info as module +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args, exit_json, fail_json + +MOCK_SUCCESSFUL_RESPONSE_WITH_ACTIONS = { + "ret": True, + "data": { + "Actions": {} + } +} + +MOCK_SUCCESSFUL_HTTP_EMPTY_RESPONSE = { + "ret": True, + "data": { + } +} + +MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE = { + "ret": True, + "data": { + "UpdateService": { + "@odata.id": "/UpdateService" + } + } +} + +MOCK_SUCCESSFUL_RESPONSE_WITH_SIMPLE_UPDATE_BUT_NO_FW_ACTIVATE = { + "ret": True, + "data": { + "Actions": { + "#UpdateService.SimpleUpdate": { + "target": "mocked value" + }, + "Oem": { + "WDC": {} # No #UpdateService.FWActivate + } + } + } +} + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + return arg + + +def get_redfish_facts(ansible_exit_json): + """From an AnsibleExitJson exception, get the redfish facts dict.""" + return ansible_exit_json.exception.args[0]["redfish_facts"] + + +def get_exception_message(ansible_exit_json): + """From an AnsibleExitJson exception, get the message string.""" + return ansible_exit_json.exception.args[0]["msg"] + + +class TestWdcRedfishInfo(unittest.TestCase): + + def setUp(self): + self.mock_module_helper = 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) + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + module.main() + + def test_module_fail_when_unknown_category(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'unknown', + 'command': 'SimpleUpdateStatus', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': [], + }) + module.main() + + def test_module_fail_when_unknown_command(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'Update', + 'command': 'unknown', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': [], + }) + module.main() + + def test_module_simple_update_status_pass(self): + set_module_args({ + 'category': 'Update', + 'command': 'SimpleUpdateStatus', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + }) + + def mock_simple_update_status(*args, **kwargs): + return { + "ret": True, + "data": { + "Description": "Ready for FW update", + "ErrorCode": 0, + "EstimatedRemainingMinutes": 0, + "StatusCode": 0 + } + } + + def mocked_string_response(*args, **kwargs): + return "mockedUrl" + + def empty_return(*args, **kwargs): + return {"ret": True} + + with patch.multiple(module.WdcRedfishUtils, + _simple_update_status_uri=mocked_string_response, + _find_updateservice_resource=empty_return, + _find_updateservice_additional_uris=empty_return, + get_request=mock_simple_update_status): + with self.assertRaises(AnsibleExitJson) as ansible_exit_json: + module.main() + redfish_facts = get_redfish_facts(ansible_exit_json) + self.assertEqual(mock_simple_update_status()["data"], + redfish_facts["simple_update_status"]["entries"]) + + def test_module_simple_update_status_updateservice_resource_not_found(self): + set_module_args({ + 'category': 'Update', + 'command': 'SimpleUpdateStatus', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + }) + with patch.object(module.WdcRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + "ret": True, + "data": {} # Missing UpdateService property + } + with self.assertRaises(AnsibleFailJson) as ansible_exit_json: + module.main() + self.assertEqual("UpdateService resource not found", + get_exception_message(ansible_exit_json)) + + def test_module_simple_update_status_service_does_not_support_simple_update(self): + set_module_args({ + 'category': 'Update', + 'command': 'SimpleUpdateStatus', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + }) + + def mock_get_request_function(uri): + mock_url_string = "mockURL" + if mock_url_string in uri: + return { + "ret": True, + "data": { + "Actions": { # No #UpdateService.SimpleUpdate + } + } + } + else: + return { + "ret": True, + "data": mock_url_string + } + + with patch.object(module.WdcRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.side_effect = mock_get_request_function + with self.assertRaises(AnsibleFailJson) as ansible_exit_json: + module.main() + self.assertEqual("UpdateService resource not found", + get_exception_message(ansible_exit_json)) + + def test_module_simple_update_status_service_does_not_support_fw_activate(self): + set_module_args({ + 'category': 'Update', + 'command': 'SimpleUpdateStatus', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'ioms': ["example1.example.com"], + }) + + def mock_get_request_function(uri): + if uri.endswith("/redfish/v1") or uri.endswith("/redfish/v1/"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_UPDATE_SERVICE_RESOURCE + elif uri.endswith("/mockedUrl"): + return MOCK_SUCCESSFUL_HTTP_EMPTY_RESPONSE + elif uri.endswith("/UpdateService"): + return MOCK_SUCCESSFUL_RESPONSE_WITH_SIMPLE_UPDATE_BUT_NO_FW_ACTIVATE + else: + raise RuntimeError("Illegal call to get_request in test: " + uri) + + with patch("ansible_collections.community.general.plugins.module_utils.wdc_redfish_utils.WdcRedfishUtils.get_request") as mock_get_request: + mock_get_request.side_effect = mock_get_request_function + with self.assertRaises(AnsibleFailJson) as ansible_exit_json: + module.main() + self.assertEqual("Service does not support FWActivate", + get_exception_message(ansible_exit_json)) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xcc_redfish_command.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_xcc_redfish_command.py new file mode 100644 index 000000000..c3902a2f3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xcc_redfish_command.py @@ -0,0 +1,629 @@ +# 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.mock import patch +from ansible_collections.community.general.tests.unit.compat import unittest +from ansible.module_utils import basic +import ansible_collections.community.general.plugins.modules.xcc_redfish_command as module +from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson +from ansible_collections.community.general.tests.unit.plugins.modules.utils import set_module_args, exit_json, fail_json + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + return arg + + +class TestXCCRedfishCommand(unittest.TestCase): + + def setUp(self): + self.mock_module_helper = 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) + + def test_module_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({}) + module.main() + + def test_module_fail_when_unknown_category(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'unknown', + 'command': 'VirtualMediaEject', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + module.main() + + def test_module_fail_when_unknown_command(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'Manager', + 'command': 'unknown', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + module.main() + + def test_module_command_VirtualMediaInsert_pass(self): + set_module_args({ + 'category': 'Manager', + 'command': 'VirtualMediaInsert', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'timeout': 30, + 'virtual_media': { + 'image_url': "nfs://10.245.52.18:/home/nfs/bootable-sr635-20210111-autorun.iso", + 'media_types': ['CD'], + 'inserted': True, + 'write_protected': True, + 'transfer_protocol_type': 'NFS' + } + }) + with patch.object(module.XCCRedfishUtils, '_find_systems_resource') as mock__find_systems_resource: + mock__find_systems_resource.return_value = {'ret': True, 'changed': True, 'msg': 'success'} + with patch.object(module.XCCRedfishUtils, '_find_managers_resource') as mock__find_managers_resource: + mock__find_managers_resource.return_value = {'ret': True, 'changed': True, 'msg': 'success'} + + with patch.object(module.XCCRedfishUtils, 'virtual_media_insert') as mock_virtual_media_insert: + mock_virtual_media_insert.return_value = {'ret': True, 'changed': True, 'msg': 'success'} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() + + def test_module_command_VirtualMediaEject_pass(self): + set_module_args({ + 'category': 'Manager', + 'command': 'VirtualMediaEject', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'timeout': 30, + 'virtual_media': { + 'image_url': "nfs://10.245.52.18:/home/nfs/bootable-sr635-20210111-autorun.iso", + } + }) + with patch.object(module.XCCRedfishUtils, '_find_systems_resource') as mock__find_systems_resource: + mock__find_systems_resource.return_value = {'ret': True, 'changed': True, 'msg': 'success'} + with patch.object(module.XCCRedfishUtils, '_find_managers_resource') as mock__find_managers_resource: + mock__find_managers_resource.return_value = {'ret': True, 'changed': True, 'msg': 'success'} + + with patch.object(module.XCCRedfishUtils, 'virtual_media_eject') as mock_virtual_media_eject: + mock_virtual_media_eject.return_value = {'ret': True, 'changed': True, 'msg': 'success'} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() + + def test_module_command_VirtualMediaEject_fail_when_required_args_missing(self): + with self.assertRaises(AnsibleFailJson): + set_module_args({ + 'category': 'Manager', + 'command': 'VirtualMediaEject', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + module.main() + + def test_module_command_GetResource_fail_when_required_args_missing(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_GetResource_fail_when_get_return_false(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': False, 'msg': '404 error'} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_GetResource_pass(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() + + def test_module_command_GetCollectionResource_fail_when_required_args_missing(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetCollectionResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_GetCollectionResource_fail_when_get_return_false(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetCollectionResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': False, 'msg': '404 error'} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_GetCollectionResource_fail_when_get_not_colection(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetCollectionResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_GetCollectionResource_pass_when_get_empty_collection(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetCollectionResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'Members': [], 'Members@odata.count': 0}} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() + + def test_module_command_GetCollectionResource_pass_when_get_collection(self): + set_module_args({ + 'category': 'Raw', + 'command': 'GetCollectionResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'Members': [{'@odata.id': '/redfish/v1/testuri/1'}], 'Members@odata.count': 1}} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() + + def test_module_command_PatchResource_fail_when_required_args_missing(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PatchResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx', '@odata.etag': '27f6eb13fa1c28a2711'}} + + with patch.object(module.XCCRedfishUtils, 'patch_request') as mock_patch_request: + mock_patch_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PatchResource_fail_when_required_args_missing_no_requestbody(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PatchResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx', '@odata.etag': '27f6eb13fa1c28a2711'}} + + with patch.object(module.XCCRedfishUtils, 'patch_request') as mock_patch_request: + mock_patch_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PatchResource_fail_when_noexisting_property_in_requestbody(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PatchResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + 'request_body': {'teststr': 'yyyy', 'otherkey': 'unknownkey'} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx', '@odata.etag': '27f6eb13fa1c28a2711'}} + + with patch.object(module.XCCRedfishUtils, 'patch_request') as mock_patch_request: + mock_patch_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx'}} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PatchResource_fail_when_get_return_false(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PatchResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + 'request_body': {'teststr': 'yyyy'} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx', '@odata.etag': '27f6eb13fa1c28a2711'}} + + with patch.object(module.XCCRedfishUtils, 'patch_request') as mock_patch_request: + mock_patch_request.return_value = {'ret': False, 'msg': '500 internal error'} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PatchResource_pass(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PatchResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + 'request_body': {'teststr': 'yyyy'} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': True, 'data': {'teststr': 'xxxx', '@odata.etag': '27f6eb13fa1c28a2711'}} + + with patch.object(module.XCCRedfishUtils, 'patch_request') as mock_patch_request: + mock_patch_request.return_value = {'ret': True, 'data': {'teststr': 'yyyy', '@odata.etag': '322e0d45d9572723c98'}} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_required_args_missing(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_invalid_resourceuri(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/testuri', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_no_requestbody(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_no_requestbody(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword', + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_requestbody_mismatch_with_data_from_actioninfo_uri(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword', + 'request_body': {'PasswordName': 'UefiAdminPassword', 'NewPassword': 'PASSW0RD=='} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Parameters': [], + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_get_return_false(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword', + 'request_body': {'PasswordName': 'UefiAdminPassword', 'NewPassword': 'PASSW0RD=='} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = {'ret': False, 'msg': '404 error'} + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_fail_when_post_return_false(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios', + 'request_body': {} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': False, 'msg': '500 internal error'} + + with self.assertRaises(AnsibleFailJson) as result: + module.main() + + def test_module_command_PostResource_pass(self): + set_module_args({ + 'category': 'Raw', + 'command': 'PostResource', + 'baseuri': '10.245.39.251', + 'username': 'USERID', + 'password': 'PASSW0RD=21', + 'resource_uri': '/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios', + 'request_body': {} + }) + + with patch.object(module.XCCRedfishUtils, 'get_request') as mock_get_request: + mock_get_request.return_value = { + 'ret': True, + 'data': { + 'Actions': { + '#Bios.ChangePassword': { + '@Redfish.ActionInfo': "/redfish/v1/Systems/1/Bios/ChangePasswordActionInfo", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ChangePassword", + 'title': "ChangePassword", + 'PasswordName@Redfish.AllowableValues': [ + "UefiAdminPassword", + "UefiPowerOnPassword" + ] + }, + '#Bios.ResetBios': { + 'title': "ResetBios", + 'target': "/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios" + } + }, + } + } + + with patch.object(module.XCCRedfishUtils, 'post_request') as mock_post_request: + mock_post_request.return_value = {'ret': True, 'msg': 'post success'} + + with self.assertRaises(AnsibleExitJson) as result: + module.main() diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_info.py new file mode 100644 index 000000000..6eb22a767 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_info.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 +import pytest + +from .xenserver_common import fake_xenapi_ref +from .xenserver_conftest import XenAPI, xenserver_guest_info # noqa: F401, pylint: disable=unused-import + +pytestmark = pytest.mark.usefixtures('patch_ansible_module') + + +testcase_module_params = { + "params": [ + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "uuid": "somevmuuid", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "uuid": "somevmuuid", + }, + ], + "ids": [ + "name", + "uuid", + "name+uuid", + ], +} + + +@pytest.mark.parametrize('patch_ansible_module', testcase_module_params['params'], ids=testcase_module_params['ids'], indirect=True) +def test_xenserver_guest_info(mocker, capfd, XenAPI, xenserver_guest_info): + """ + Tests regular module invocation including parsing and propagation of + module params and module output. + """ + fake_vm_facts = {"fake-vm-fact": True} + + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_info.get_object_ref', return_value=None) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_info.gather_vm_params', return_value=None) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_info.gather_vm_facts', return_value=fake_vm_facts) + + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "pool.get_all.return_value": [fake_xenapi_ref('pool')], + "pool.get_default_SR.return_value": fake_xenapi_ref('SR'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.get_xenserver_version', return_value=[7, 2, 0]) + + with pytest.raises(SystemExit): + xenserver_guest_info.main() + + out, err = capfd.readouterr() + result = json.loads(out) + + assert result['instance'] == fake_vm_facts diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_powerstate.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_powerstate.py new file mode 100644 index 000000000..74b21fcf3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xenserver_guest_powerstate.py @@ -0,0 +1,299 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 +import pytest + +from .xenserver_common import fake_xenapi_ref +from .xenserver_conftest import fake_ansible_module, XenAPI, xenserver_guest_powerstate # noqa: F401, pylint: disable=unused-import + + +testcase_set_powerstate = { + "params": [ + (False, "someoldstate"), + (True, "somenewstate"), + ], + "ids": [ + "state-same", + "state-changed", + ], +} + +testcase_module_params_state_present = { + "params": [ + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "present", + }, + ], + "ids": [ + "present-implicit", + "present-explicit", + ], +} + +testcase_module_params_state_other = { + "params": [ + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "powered-on", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "powered-off", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "restarted", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "shutdown-guest", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "reboot-guest", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "suspended", + }, + ], + "ids": [ + "powered-on", + "powered-off", + "restarted", + "shutdown-guest", + "reboot-guest", + "suspended", + ], +} + +testcase_module_params_wait = { + "params": [ + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "present", + "wait_for_ip_address": "yes", + }, + { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "name": "somevmname", + "state": "powered-on", + "wait_for_ip_address": "yes", + }, + ], + "ids": [ + "wait-present", + "wait-other", + ], +} + + +@pytest.mark.parametrize('power_state', testcase_set_powerstate['params'], ids=testcase_set_powerstate['ids']) +def test_xenserver_guest_powerstate_set_power_state(mocker, fake_ansible_module, XenAPI, xenserver_guest_powerstate, power_state): + """Tests power state change handling.""" + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.get_object_ref', + return_value=fake_xenapi_ref('VM')) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_params', + return_value={"power_state": "Someoldstate"}) + mocked_set_vm_power_state = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.set_vm_power_state', + return_value=power_state) + + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "pool.get_all.return_value": [fake_xenapi_ref('pool')], + "pool.get_default_SR.return_value": fake_xenapi_ref('SR'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.get_xenserver_version', return_value=[7, 2, 0]) + + fake_ansible_module.params.update({ + "name": "somename", + "uuid": "someuuid", + "state_change_timeout": 1, + }) + + vm = xenserver_guest_powerstate.XenServerVM(fake_ansible_module) + state_changed = vm.set_power_state(None) + + mocked_set_vm_power_state.assert_called_once_with(fake_ansible_module, fake_xenapi_ref('VM'), None, 1) + assert state_changed == power_state[0] + assert vm.vm_params['power_state'] == power_state[1].capitalize() + + +@pytest.mark.parametrize('patch_ansible_module', + testcase_module_params_state_present['params'], + ids=testcase_module_params_state_present['ids'], + indirect=True) +def test_xenserver_guest_powerstate_present(mocker, patch_ansible_module, capfd, XenAPI, xenserver_guest_powerstate): + """ + Tests regular module invocation including parsing and propagation of + module params and module output when state is set to present. + """ + fake_vm_facts = {"fake-vm-fact": True} + + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.get_object_ref', + return_value=fake_xenapi_ref('VM')) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_params', return_value={}) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_facts', + return_value=fake_vm_facts) + mocked_set_vm_power_state = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.set_vm_power_state', + return_value=(True, "somenewstate")) + mocked_wait_for_vm_ip_address = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.wait_for_vm_ip_address', + return_value={}) + + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "pool.get_all.return_value": [fake_xenapi_ref('pool')], + "pool.get_default_SR.return_value": fake_xenapi_ref('SR'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.get_xenserver_version', return_value=[7, 2, 0]) + + with pytest.raises(SystemExit): + xenserver_guest_powerstate.main() + + out, err = capfd.readouterr() + result = json.loads(out) + + mocked_set_vm_power_state.assert_not_called() + mocked_wait_for_vm_ip_address.assert_not_called() + assert result['changed'] is False + assert result['instance'] == fake_vm_facts + + +@pytest.mark.parametrize('patch_ansible_module', + testcase_module_params_state_other['params'], + ids=testcase_module_params_state_other['ids'], + indirect=True) +def test_xenserver_guest_powerstate_other(mocker, patch_ansible_module, capfd, XenAPI, xenserver_guest_powerstate): + """ + Tests regular module invocation including parsing and propagation of + module params and module output when state is set to other value than + present. + """ + fake_vm_facts = {"fake-vm-fact": True} + + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.get_object_ref', + return_value=fake_xenapi_ref('VM')) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_params', return_value={}) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_facts', return_value=fake_vm_facts) + mocked_set_vm_power_state = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.set_vm_power_state', + return_value=(True, "somenewstate")) + mocked_wait_for_vm_ip_address = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.wait_for_vm_ip_address', + return_value={}) + + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "pool.get_all.return_value": [fake_xenapi_ref('pool')], + "pool.get_default_SR.return_value": fake_xenapi_ref('SR'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.get_xenserver_version', return_value=[7, 2, 0]) + + with pytest.raises(SystemExit): + xenserver_guest_powerstate.main() + + out, err = capfd.readouterr() + result = json.loads(out) + + mocked_set_vm_power_state.assert_called_once() + mocked_wait_for_vm_ip_address.assert_not_called() + assert result['changed'] is True + assert result['instance'] == fake_vm_facts + + +@pytest.mark.parametrize('patch_ansible_module', + testcase_module_params_wait['params'], + ids=testcase_module_params_wait['ids'], + indirect=True) +def test_xenserver_guest_powerstate_wait(mocker, patch_ansible_module, capfd, XenAPI, xenserver_guest_powerstate): + """ + Tests regular module invocation including parsing and propagation of + module params and module output when wait_for_ip_address option is used. + """ + fake_vm_facts = {"fake-vm-fact": True} + + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.get_object_ref', + return_value=fake_xenapi_ref('VM')) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_params', return_value={}) + mocker.patch('ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.gather_vm_facts', return_value=fake_vm_facts) + mocked_set_vm_power_state = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.set_vm_power_state', + return_value=(True, "somenewstate")) + mocked_wait_for_vm_ip_address = mocker.patch( + 'ansible_collections.community.general.plugins.modules.xenserver_guest_powerstate.wait_for_vm_ip_address', + return_value={}) + + mocked_xenapi = mocker.patch.object(XenAPI.Session, 'xenapi', create=True) + + mocked_returns = { + "pool.get_all.return_value": [fake_xenapi_ref('pool')], + "pool.get_default_SR.return_value": fake_xenapi_ref('SR'), + } + + mocked_xenapi.configure_mock(**mocked_returns) + + mocker.patch('ansible_collections.community.general.plugins.module_utils.xenserver.get_xenserver_version', return_value=[7, 2, 0]) + + with pytest.raises(SystemExit): + xenserver_guest_powerstate.main() + + out, err = capfd.readouterr() + result = json.loads(out) + + mocked_wait_for_vm_ip_address.assert_called_once() + assert result['instance'] == fake_vm_facts 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 new file mode 100644 index 000000000..c979fd8d2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py @@ -0,0 +1,312 @@ +# -*- coding: utf-8 -*- +# Author: Alexei Znamensky (russoz@gmail.com) +# Largely adapted from test_redhat_subscription by +# Jiri Hnidek (jhnidek@redhat.com) +# +# Copyright (c) Alexei Znamensky (russoz@gmail.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 json + +from ansible_collections.community.general.plugins.modules import xfconf + +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]) 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 new file mode 100644 index 000000000..dfcd4f33a --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py @@ -0,0 +1,172 @@ +# 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 + +import json + +from ansible_collections.community.general.plugins.modules import xfconf_info + +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 diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/utils.py b/ansible_collections/community/general/tests/unit/plugins/modules/utils.py new file mode 100644 index 000000000..1f7f14722 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/utils.py @@ -0,0 +1,54 @@ +# 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 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 + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class ModuleTestCase(unittest.TestCase): + + def setUp(self): + self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json) + self.mock_module.start() + self.mock_sleep = patch('time.sleep') + self.mock_sleep.start() + set_module_args({}) + self.addCleanup(self.mock_module.stop) + self.addCleanup(self.mock_sleep.stop) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/xenserver_common.py b/ansible_collections/community/general/tests/unit/plugins/modules/xenserver_common.py new file mode 100644 index 000000000..d3ebb484d --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/xenserver_common.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 + + +def fake_xenapi_ref(xenapi_class): + return "OpaqueRef:fake-xenapi-%s-ref" % xenapi_class diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/xenserver_conftest.py b/ansible_collections/community/general/tests/unit/plugins/modules/xenserver_conftest.py new file mode 100644 index 000000000..f003be8b2 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/xenserver_conftest.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Bojan Vitnik +# 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 importlib +import pytest + +from .FakeAnsibleModule import FakeAnsibleModule + + +@pytest.fixture +def fake_ansible_module(request): + """Returns fake AnsibleModule with fake module params.""" + if hasattr(request, 'param'): + return FakeAnsibleModule(request.param) + else: + params = { + "hostname": "somehost", + "username": "someuser", + "password": "somepwd", + "validate_certs": True, + } + + return FakeAnsibleModule(params) + + +@pytest.fixture(autouse=True) +def XenAPI(): + """Imports and returns fake XenAPI module.""" + + # Import of fake XenAPI module is wrapped by fixture so that it does not + # affect other unit tests which could potentially also use XenAPI module. + + # First we use importlib.import_module() to import the module and assign + # it to a local symbol. + fake_xenapi = importlib.import_module('ansible_collections.community.general.tests.unit.plugins.modules.FakeXenAPI') + + # Now we populate Python module cache with imported fake module using the + # original module name (XenAPI). That way, any 'import XenAPI' statement + # will just load already imported fake module from the cache. + sys.modules['XenAPI'] = fake_xenapi + + return fake_xenapi + + +@pytest.fixture +def xenserver_guest_info(XenAPI): + """Imports and returns xenserver_guest_info module.""" + + # Since we are wrapping fake XenAPI module inside a fixture, all modules + # that depend on it have to be imported inside a test function. To make + # this easier to handle and remove some code repetition, we wrap the import + # of xenserver_guest_info module with a fixture. + from ansible_collections.community.general.plugins.modules import xenserver_guest_info + + return xenserver_guest_info + + +@pytest.fixture +def xenserver_guest_powerstate(XenAPI): + """Imports and returns xenserver_guest_powerstate module.""" + + # Since we are wrapping fake XenAPI module inside a fixture, all modules + # that depend on it have to be imported inside a test function. To make + # this easier to handle and remove some code repetition, we wrap the import + # of xenserver_guest_powerstate module with a fixture. + from ansible_collections.community.general.plugins.modules import xenserver_guest_powerstate + + return xenserver_guest_powerstate diff --git a/ansible_collections/community/general/tests/unit/requirements.txt b/ansible_collections/community/general/tests/unit/requirements.txt new file mode 100644 index 000000000..0aa7c1fc9 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/requirements.txt @@ -0,0 +1,46 @@ +# 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 + +unittest2 ; python_version < '2.7' +importlib ; python_version < '2.7' + +# requirement for the memcached cache plugin +python-memcached + +# requirement for the redis cache plugin +redis + +# requirement for the linode module +linode-python # APIv3 +linode_api4 ; python_version > '2.6' # APIv4 + +# requirement for the gitlab and github modules +python-gitlab +PyGithub +httmock + +# requirement for maven_artifact module +lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later +lxml ; python_version >= '2.7' +semantic_version + +# requirement for datadog_downtime module +datadog-api-client >= 1.0.0b3 ; python_version >= '3.6' + +# requirement for dnsimple module +dnsimple >= 2 ; python_version >= '3.6' +dataclasses ; python_version == '3.6' + +# requirement for the opentelemetry callback plugin +# WARNING: these libraries rely on Protobuf for Python, which regularly stops installing. +# That's why they are disabled for now. +# opentelemetry-api ; python_version >= '3.6' and python_version < '3.10' +# opentelemetry-exporter-otlp ; python_version >= '3.6' and python_version < '3.10' +# opentelemetry-sdk ; python_version >= '3.6' and python_version < '3.10' + +# requirement for the elastic callback plugin +elastic-apm ; python_version >= '3.6' + +# requirements for scaleway modules +passlib[argon2] \ No newline at end of file diff --git a/ansible_collections/community/general/tests/unit/requirements.yml b/ansible_collections/community/general/tests/unit/requirements.yml new file mode 100644 index 000000000..586a6a1b3 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/requirements.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 + +collections: +- community.internal_test_tools diff --git a/ansible_collections/community/general/tests/utils/constraints.txt b/ansible_collections/community/general/tests/utils/constraints.txt new file mode 100644 index 000000000..4fb5276e2 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/constraints.txt @@ -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 + +certifi < 2022.5.18 ; python_version < '3.5' # certifi 2022.5.18 requires Python 3.5 or later +coverage >= 4.2, < 5.0.0, != 4.3.2 ; python_version <= '3.7' # features in 4.2+ required, avoid known bug in 4.3.2 on python 2.6, coverage 5.0+ incompatible +coverage >= 4.5.4, < 5.0.0 ; python_version > '3.7' # coverage had a bug in < 4.5.4 that would cause unit tests to hang in Python 3.8, coverage 5.0+ incompatible +cryptography < 2.2 ; python_version < '2.7' # cryptography 2.2 drops support for python 2.6 +cryptography >= 3.0, < 3.4 ; python_version < '3.6' and python_version >= '2.7' # cryptography 3.4 drops support for python 2.7 +cryptography >= 3.3, < 3.4 ; python_version >= '2.7' and python_version < '3.9' # FIXME: the upper limit is needed for RHEL8.2, CentOS 8, Ubuntu 18.04, and OpenSuSE 15 +deepdiff < 4.0.0 ; python_version < '3' # deepdiff 4.0.0 and later require python 3 +jinja2 < 2.11 ; python_version < '2.7' # jinja2 2.11 and later require python 2.7 or later +urllib3 < 1.24 ; python_version < '2.7' # urllib3 1.24 and later require python 2.7 or later +pywinrm >= 0.3.0 # message encryption support +sphinx < 1.6 ; python_version < '2.7' # sphinx 1.6 and later require python 2.7 or later +sphinx < 1.8 ; python_version >= '2.7' # sphinx 1.8 and later are currently incompatible with rstcheck 3.3 +pygments >= 2.4.0 # Pygments 2.4.0 includes bugfixes for YAML and YAML+Jinja lexers +wheel < 0.30.0 ; python_version < '2.7' # wheel 0.30.0 and later require python 2.7 or later +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 +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 +pytest < 5.0.0 ; python_version == '2.7' # pytest 5.0.0 and later will no longer support python 2.7 +pytest-forked < 1.0.2 ; python_version < '2.7' # pytest-forked 1.0.2 and later require python 2.7 or later +pytest-forked >= 1.0.2 ; python_version >= '2.7' # pytest-forked before 1.0.2 does not work with pytest 4.2.0+ (which requires python 2.7+) +ntlm-auth >= 1.3.0 # message encryption support using cryptography +requests < 2.20.0 ; python_version < '2.7' # requests 2.20.0 drops support for python 2.6 +requests < 2.28 ; python_version >= '2.7' and python_version < '3.7' # requests 2.28.0 drops support for python 3.6 and before +requests-ntlm >= 1.1.0 # message encryption support +requests-credssp >= 0.1.0 # message encryption support +voluptuous >= 0.11.0 # Schema recursion via Self +openshift >= 0.6.2, < 0.9.0 # merge_type support +virtualenv < 16.0.0 ; python_version < '2.7' # virtualenv 16.0.0 and later require python 2.7 or later +pathspec < 0.6.0 ; python_version < '2.7' # pathspec 0.6.0 and later require python 2.7 or later +pyopenssl < 18.0.0 ; python_version < '2.7' # pyOpenSSL 18.0.0 and later require python 2.7 or later +pyopenssl < 22.0.0 ; python_version >= '2.7' and python_version < '3.6' # pyOpenSSL 22.0.0 and later require python 3.6 or later +pyfmg == 0.6.1 # newer versions do not pass current unit tests +pyyaml < 5.1 ; python_version < '2.7' # pyyaml 5.1 and later require python 2.7 or later +pycparser < 2.19 ; python_version < '2.7' # pycparser 2.19 and later require python 2.7 or later +mock >= 2.0.0 # needed for features backported from Python 3.6 unittest.mock (assert_called, assert_called_once...) +pytest-mock >= 1.4.0 # needed for mock_use_standalone_module pytest option +xmltodict < 0.12.0 ; python_version < '2.7' # xmltodict 0.12.0 and later require python 2.7 or later +lxml < 4.3.0 ; python_version < '2.7' # lxml 4.3.0 and later require python 2.7 or later +pyvmomi < 6.0.0 ; python_version < '2.7' # pyvmomi 6.0.0 and later require python 2.7 or later +pyone == 1.1.9 # newer versions do not pass current integration tests +boto3 < 1.11 ; python_version < '2.7' # boto3 1.11 drops Python 2.6 support +botocore >= 1.10.0, < 1.14 ; python_version < '2.7' # adds support for the following AWS services: secretsmanager, fms, and acm-pca; botocore 1.14 drops Python 2.6 support +botocore >= 1.10.0 ; python_version >= '2.7' # adds support for the following AWS services: secretsmanager, fms, and acm-pca +setuptools < 45 ; python_version <= '2.7' # setuptools 45 and later require python 3.5 or later +cffi >= 1.14.2, != 1.14.3 # Yanked version which older versions of pip will still install: +redis == 2.10.6 ; python_version < '2.7' +redis < 4.0.0 ; python_version >= '2.7' and python_version < '3.6' +redis ; python_version >= '3.6' +pycdlib < 1.13.0 ; python_version < '3' # 1.13.0 does not work with Python 2, while not declaring that +python-daemon <= 2.3.0 ; python_version < '3' +bcrypt < 4.0.0 # TEMP: restrict to < 4.0.0 since installing 4.0.0 fails on RHEL 8 diff --git a/ansible_collections/community/general/tests/utils/shippable/aix.sh b/ansible_collections/community/general/tests/utils/shippable/aix.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/aix.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/alpine.sh b/ansible_collections/community/general/tests/utils/shippable/alpine.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/alpine.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/fedora.sh b/ansible_collections/community/general/tests/utils/shippable/fedora.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/fedora.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/freebsd.sh b/ansible_collections/community/general/tests/utils/shippable/freebsd.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/freebsd.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/generic.sh b/ansible_collections/community/general/tests/utils/shippable/generic.sh new file mode 100755 index 000000000..5fd1fb55a --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/generic.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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +python="${args[1]}" +group="${args[2]}" + +target="azp/generic/${group}/" + +stage="${S:-prod}" + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote-terminate always --remote-stage "${stage}" \ + --docker --python "${python}" diff --git a/ansible_collections/community/general/tests/utils/shippable/linux-community.sh b/ansible_collections/community/general/tests/utils/shippable/linux-community.sh new file mode 100755 index 000000000..48d0d8687 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/linux-community.sh @@ -0,0 +1,22 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +image="${args[1]}" +python="${args[2]}" + +if [ "${#args[@]}" -gt 3 ]; then + target="azp/posix/${args[3]}/" +else + target="azp/posix/" +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --docker "quay.io/ansible-community/test-image:${image}" --python "${python}" diff --git a/ansible_collections/community/general/tests/utils/shippable/linux.sh b/ansible_collections/community/general/tests/utils/shippable/linux.sh new file mode 100755 index 000000000..6e1e2350b --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/linux.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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +image="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --docker "${image}" diff --git a/ansible_collections/community/general/tests/utils/shippable/macos.sh b/ansible_collections/community/general/tests/utils/shippable/macos.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/macos.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/osx.sh b/ansible_collections/community/general/tests/utils/shippable/osx.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/osx.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/remote.sh b/ansible_collections/community/general/tests/utils/shippable/remote.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/remote.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/rhel.sh b/ansible_collections/community/general/tests/utils/shippable/rhel.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/rhel.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/sanity.sh b/ansible_collections/community/general/tests/utils/shippable/sanity.sh new file mode 100755 index 000000000..5b88a2677 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/sanity.sh @@ -0,0 +1,45 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +group="${args[1]}" + +if [ "${BASE_BRANCH:-}" ]; then + base_branch="origin/${BASE_BRANCH}" +else + base_branch="" +fi + +if [ "${group}" == "extra" ]; then + ../internal_test_tools/tools/run.py --color --bot --junit + exit +fi + +case "${group}" in + 1) options=(--skip-test pylint --skip-test ansible-doc --skip-test validate-modules) ;; + 2) options=( --test ansible-doc --test validate-modules) ;; + 3) options=(--test pylint plugins/modules/) ;; + 4) options=(--test pylint --exclude plugins/modules/) ;; +esac + +# allow collection migration sanity tests for groups 3 and 4 to pass without updating this script during migration +network_path="lib/ansible/modules/network/" + +if [ -d "${network_path}" ]; then + if [ "${group}" -eq 3 ]; then + options+=(--exclude "${network_path}") + elif [ "${group}" -eq 4 ]; then + options+=("${network_path}") + fi +fi + +# shellcheck disable=SC2086 +ansible-test sanity --color -v --junit ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \ + --docker --base-branch "${base_branch}" \ + "${options[@]}" --allow-disabled diff --git a/ansible_collections/community/general/tests/utils/shippable/shippable.sh b/ansible_collections/community/general/tests/utils/shippable/shippable.sh new file mode 100755 index 000000000..e98680438 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/shippable.sh @@ -0,0 +1,232 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +ansible_version="${args[0]}" +script="${args[1]}" + +function join { + local IFS="$1"; + shift; + echo "$*"; +} + +# Ensure we can write other collections to this dir +sudo chown "$(whoami)" "${PWD}/../../" + +test="$(join / "${args[@]:1}")" + +docker images ansible/ansible +docker images quay.io/ansible/* +docker ps + +for container in $(docker ps --format '{{.Image}} {{.ID}}' | grep -v -e '^drydock/' -e '^quay.io/ansible/azure-pipelines-test-container:' | sed 's/^.* //'); do + docker rm -f "${container}" || true # ignore errors +done + +docker ps + +if [ -d /home/shippable/cache/ ]; then + ls -la /home/shippable/cache/ +fi + +command -v python +python -V + +function retry +{ + # shellcheck disable=SC2034 + for repetition in 1 2 3; do + set +e + "$@" + result=$? + set -e + if [ ${result} == 0 ]; then + return ${result} + fi + echo "@* -> ${result}" + done + echo "Command '@*' failed 3 times!" + exit 255 +} + +command -v pip +pip --version +pip list --disable-pip-version-check +if [ "${ansible_version}" == "devel" ]; then + retry pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check +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 + +if [ "${test}" == "sanity/extra" ]; then + retry pip install junit-xml --disable-pip-version-check +fi + +# START: HACK install dependencies +if [ "${script}" != "sanity" ] || [ "${test}" == "sanity/extra" ]; then + # Nothing further should be added to this list. + # This is to prevent modules or plugins in this collection having a runtime dependency on other collections. + retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.internal_test_tools.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/internal_test_tools" + retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.docker.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/docker" + # NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429) + # retry ansible-galaxy -vvv collection install community.internal_test_tools +fi + +if [ "${script}" != "sanity" ] && [ "${script}" != "units" ] && [ "${test}" != "sanity/extra" ]; then + # To prevent Python dependencies on other collections only install other collections for integration tests + retry git clone --depth=1 --single-branch https://github.com/ansible-collections/ansible.posix.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/ansible/posix" + retry git clone --depth=1 --single-branch https://github.com/ansible-collections/community.crypto.git "${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/crypto" + # NOTE: we're installing with git to work around Galaxy being a huge PITA (https://github.com/ansible/galaxy/issues/2429) + # retry ansible-galaxy -vvv collection install ansible.posix + # retry ansible-galaxy -vvv collection install community.crypto +fi + +# END: HACK + +export PYTHONIOENCODING='utf-8' + +if [ "${JOB_TRIGGERED_BY_NAME:-}" == "nightly-trigger" ]; then + COVERAGE=yes + COMPLETE=yes +fi + +if [ -n "${COVERAGE:-}" ]; then + # on-demand coverage reporting triggered by setting the COVERAGE environment variable to a non-empty value + export COVERAGE="--coverage" +elif [[ "${COMMIT_MESSAGE}" =~ ci_coverage ]]; then + # on-demand coverage reporting triggered by having 'ci_coverage' in the latest commit message + export COVERAGE="--coverage" +else + # on-demand coverage reporting disabled (default behavior, always-on coverage reporting remains enabled) + export COVERAGE="--coverage-check" +fi + +if [ -n "${COMPLETE:-}" ]; then + # disable change detection triggered by setting the COMPLETE environment variable to a non-empty value + export CHANGED="" +elif [[ "${COMMIT_MESSAGE}" =~ ci_complete ]]; then + # disable change detection triggered by having 'ci_complete' in the latest commit message + export CHANGED="" +else + # enable change detection (default behavior) + export CHANGED="--changed" +fi + +if [ "${IS_PULL_REQUEST:-}" == "true" ]; then + # run unstable tests which are targeted by focused changes on PRs + export UNSTABLE="--allow-unstable-changed" +else + # do not run unstable tests outside PRs + export UNSTABLE="" +fi + +# remove empty core/extras module directories from PRs created prior to the repo-merge +find plugins -type d -empty -print -delete + +function cleanup +{ + # for complete on-demand coverage generate a report for all files with no coverage on the "sanity/5" job so we only have one copy + if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ] && [ "${test}" == "sanity/5" ]; then + stub="--stub" + # trigger coverage reporting for stubs even if no other coverage data exists + mkdir -p tests/output/coverage/ + else + stub="" + fi + + if [ -d tests/output/coverage/ ]; then + if find tests/output/coverage/ -mindepth 1 -name '.*' -prune -o -print -quit | grep -q .; then + process_coverage='yes' # process existing coverage files + elif [ "${stub}" ]; then + process_coverage='yes' # process coverage when stubs are enabled + else + process_coverage='' + fi + + if [ "${process_coverage}" ]; then + # use python 3.7 for coverage to avoid running out of memory during coverage xml processing + # only use it for coverage to avoid the additional overhead of setting up a virtual environment for a potential no-op job + virtualenv --python /usr/bin/python3.7 ~/ansible-venv + set +ux + . ~/ansible-venv/bin/activate + set -ux + + # shellcheck disable=SC2086 + ansible-test coverage xml --color -v --requirements --group-by command --group-by version ${stub:+"$stub"} + cp -a tests/output/reports/coverage=*.xml "$SHIPPABLE_RESULT_DIR/codecoverage/" + + if [ "${ansible_version}" != "2.9" ]; then + # analyze and capture code coverage aggregated by integration test target + ansible-test coverage analyze targets generate -v "$SHIPPABLE_RESULT_DIR/testresults/coverage-analyze-targets.json" + fi + + # upload coverage report to codecov.io only when using complete on-demand coverage + if [ "${COVERAGE}" == "--coverage" ] && [ "${CHANGED}" == "" ]; then + for file in tests/output/reports/coverage=*.xml; do + flags="${file##*/coverage=}" + flags="${flags%-powershell.xml}" + flags="${flags%.xml}" + # remove numbered component from stub files when converting to tags + flags="${flags//stub-[0-9]*/stub}" + flags="${flags//=/,}" + flags="${flags//[^a-zA-Z0-9_,]/_}" + + bash <(curl -s https://ansible-ci-files.s3.us-east-1.amazonaws.com/codecov/codecov.sh) \ + -f "${file}" \ + -F "${flags}" \ + -n "${test}" \ + -t 20636cf5-4d6a-4b9a-8d2d-6f22ebbaa752 \ + -X coveragepy \ + -X gcov \ + -X fix \ + -X search \ + -X xcode \ + || echo "Failed to upload code coverage report to codecov.io: ${file}" + done + fi + fi + fi + + if [ -d tests/output/junit/ ]; then + cp -aT tests/output/junit/ "$SHIPPABLE_RESULT_DIR/testresults/" + fi + + if [ -d tests/output/data/ ]; then + cp -a tests/output/data/ "$SHIPPABLE_RESULT_DIR/testresults/" + fi + + if [ -d tests/output/bot/ ]; then + cp -aT tests/output/bot/ "$SHIPPABLE_RESULT_DIR/testresults/" + fi +} + +if [ "${SHIPPABLE_BUILD_ID:-}" ]; then trap cleanup EXIT; fi + +if [[ "${COVERAGE:-}" == "--coverage" ]]; then + timeout=60 +else + timeout=50 +fi + +ansible-test env --dump --show --timeout "${timeout}" --color -v + +if [ "${SHIPPABLE_BUILD_ID:-}" ]; then "tests/utils/shippable/check_matrix.py"; fi +"tests/utils/shippable/${script}.sh" "${test}" "${ansible_version}" diff --git a/ansible_collections/community/general/tests/utils/shippable/ubuntu.sh b/ansible_collections/community/general/tests/utils/shippable/ubuntu.sh new file mode 100755 index 000000000..84c1ebbe0 --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/ubuntu.sh @@ -0,0 +1,29 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +platform="${args[0]}" +version="${args[1]}" + +if [ "${#args[@]}" -gt 2 ]; then + target="azp/posix/${args[2]}/" +else + target="azp/posix/" +fi + +stage="${S:-prod}" +provider="${P:-default}" + +if [ "${platform}" == "rhel" ] && [[ "${version}" =~ ^8 ]]; then + echo "pynacl >= 1.4.0, < 1.5.0; python_version == '3.6'" >> tests/utils/constraints.txt +fi + +# shellcheck disable=SC2086 +ansible-test integration --color -v --retry-on-error "${target}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} ${UNSTABLE:+"$UNSTABLE"} \ + --remote "${platform}/${version}" --remote-terminate always --remote-stage "${stage}" --remote-provider "${provider}" diff --git a/ansible_collections/community/general/tests/utils/shippable/units.sh b/ansible_collections/community/general/tests/utils/shippable/units.sh new file mode 100755 index 000000000..f591ec25a --- /dev/null +++ b/ansible_collections/community/general/tests/utils/shippable/units.sh @@ -0,0 +1,41 @@ +#!/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 -o pipefail -eux + +declare -a args +IFS='/:' read -ra args <<< "$1" + +version="${args[1]}" +group="${args[2]}" + +if [[ "${COVERAGE:-}" == "--coverage" ]]; then + timeout=90 +else + timeout=30 +fi + +group1=() + +case "${group}" in + 1) options=("${group1[@]:+${group1[@]}}") ;; +esac + +ansible-test env --timeout "${timeout}" --color -v + +if [ "$2" == "2.9" ]; then + # 1.5.0+ will not install for Python 3.6+ in the 2.9 setting (due to `enum` being installed) + echo "pynacl >= 1.4.0, < 1.5.0; python_version >= '3.6'" >> tests/unit/requirements.txt +fi + +if [ "$2" == "2.10" ]; then + sed -i -E 's/^redis($| .*)/redis < 4.1.0/g' tests/unit/requirements.txt + sed -i -E 's/^python-gitlab($| .*)/python-gitlab < 2.10.1 ; python_version >= '\'3.6\''/g' tests/unit/requirements.txt + echo "python-gitlab ; python_version < '3.6'" >> tests/unit/requirements.txt +fi + +# shellcheck disable=SC2086 +ansible-test units --color -v --docker default --python "${version}" ${COVERAGE:+"$COVERAGE"} ${CHANGED:+"$CHANGED"} \ + "${options[@]:+${options[@]}}" \ -- cgit v1.2.3