summaryrefslogtreecommitdiffstats
path: root/ansible_collections/sensu/sensu_go/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/sensu/sensu_go/tests
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/sensu/sensu_go/tests')
-rw-r--r--ansible_collections/sensu/sensu_go/tests/config.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/base.yml54
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml102
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml313
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml13
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml67
-rwxr-xr-xansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh14
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt19
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf9
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key27
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml13
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml360
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml260
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml230
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml171
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml219
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml194
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml133
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml191
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml181
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml217
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml150
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml109
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml139
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml358
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml144
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml78
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml276
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml129
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml220
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml230
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml171
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml127
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml272
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt19
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt21
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key28
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml180
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml153
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml53
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml198
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml0
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml134
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml14
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml24
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml5
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml30
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml28
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml71
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml21
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml74
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml14
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml24
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml8
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml35
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml164
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt87
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key28
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt87
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key28
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt20
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt87
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key28
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt87
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key28
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml18
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml24
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml50
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml11
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml31
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml11
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml13
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml35
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml12
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml13
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml30
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml12
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml39
-rw-r--r--ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml13
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt4
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt4
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt4
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt4
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt4
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt4
-rw-r--r--ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt3
-rwxr-xr-xansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py62
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py316
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py54
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py58
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py158
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py152
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py295
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py169
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py285
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py491
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py53
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py332
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py251
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py47
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py327
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py83
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py85
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py128
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py152
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py269
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py199
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py132
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py85
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py286
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py94
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py91
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py59
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py93
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py326
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py126
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py49
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py41
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py118
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py136
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py142
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py214
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py85
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py86
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py69
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py182
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py120
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py66
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py101
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py105
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py519
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/requirements.txt1
181 files changed, 15687 insertions, 0 deletions
diff --git a/ansible_collections/sensu/sensu_go/tests/config.yml b/ansible_collections/sensu/sensu_go/tests/config.yml
new file mode 100644
index 000000000..59769d374
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/config.yml
@@ -0,0 +1,3 @@
+---
+modules:
+ python_requires: ">= 2.7"
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/base.yml b/ansible_collections/sensu/sensu_go/tests/integration/base.yml
new file mode 100644
index 000000000..396c4ebfe
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/base.yml
@@ -0,0 +1,54 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - converge
+ - destroy
+dependency:
+ name: galaxy
+driver:
+ name: docker
+provisioner:
+ name: ansible
+ config_options:
+ defaults:
+ interpreter_python: auto_silent
+ lint:
+ enabled: false
+platforms:
+ - name: v6.9.0
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.9.0
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v6.8.2
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.8.2
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v6.7.5
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.7.5
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v6.6.2
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.6.2
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v6.5.5
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.5.5
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v6.4.3
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.4.3
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v6.3.0
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.3.0
+ pre_build_image: true
+ pull: true
+ override_command: false
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml
new file mode 100644
index 000000000..ceeb3bdcd
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/converge.yml
@@ -0,0 +1,102 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create a Bonsai asset
+ bonsai_asset: &idempotence
+ auth:
+ url: http://localhost:8080
+ name: sensu/monitoring-plugins
+ version: 2.2.0-1
+ on_remote: true
+ register: result
+
+ - assert:
+ that:
+ # https://bonsai.sensu.io/api/v1/assets/sensu/monitoring-plugins/2.2.0-1/release_asset_builds
+ - result is changed
+ - result.object.metadata.name == 'sensu/monitoring-plugins'
+ - result.object.builds | length == 3
+ - result.object.metadata.annotations | dict2items | length == 7
+
+ - name: Test asset creation idempotence
+ bonsai_asset: *idempotence
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify an asset
+ bonsai_asset:
+ auth:
+ url: http://localhost:8080
+ name: sensu/monitoring-plugins
+ version: 2.2.0-2
+ labels:
+ my: label
+ annotations:
+ anot: here
+ register: result
+
+ - assert:
+ that:
+ # https://bonsai.sensu.io/api/v1/assets/sensu/monitoring-plugins/2.2.0-2/release_asset_builds
+ - result is changed
+ - result.object.metadata.name == 'sensu/monitoring-plugins'
+ - result.object.builds | length == 4
+ - result.object.metadata.annotations | dict2items | length == 8
+ - result.object.metadata.annotations.anot == 'here'
+ - result.object.metadata.labels.my == 'label'
+ - result is changed
+
+ - name: Fetch a specific asset
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ name: sensu/monitoring-plugins
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'sensu/monitoring-plugins'
+
+ - name: Add same asset under different name
+ bonsai_asset:
+ auth:
+ url: http://localhost:8080
+ name: sensu/monitoring-plugins
+ version: 2.2.0-2
+ rename: renamed-asset
+
+ - name: Fetch renamed asset
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ name: renamed-asset
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'renamed-asset'
+
+ - name: Delete an asset
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: sensu/monitoring-plugins
+ state: absent
+
+ - name: Fetch all assets again after deletion
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/action_bonsai_asset/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml
new file mode 100644
index 000000000..5cc1fb19f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/converge.yml
@@ -0,0 +1,313 @@
+---
+- name: Prepare new backend
+ hosts: has_api_key_support
+ gather_facts: no
+
+ tasks:
+ - name: Create a regular user
+ sensu.sensu_go.user:
+ name: test_user
+ password: test_pass
+
+ - name: Create a regular user role
+ sensu.sensu_go.role:
+ name: test_role
+ rules:
+ - verbs: [ list ]
+ resources: [ assets ]
+
+ - name: Allow user to list entities
+ sensu.sensu_go.role_binding:
+ name: test_binding
+ role: test_role
+ users: [ test_user ]
+
+ - name: Configure sensuctl
+ command:
+ cmd: >
+ sensuctl configure
+ --non-interactive
+ --url http://localhost:8080
+ --username admin
+ --password P@ssw0rd!
+ --namespace default
+
+ - name: Create API key for admin
+ command:
+ cmd: sensuctl api-key grant admin
+ register: api_key_admin_result
+
+ - name: Create API key for test user
+ command:
+ cmd: sensuctl api-key grant test_user
+ register: api_key_test_user_result
+
+ - name: Store API keys
+ set_fact:
+ api_key_admin: "{{ api_key_admin_result.stdout | regex_search('[^/]+$') }}"
+ api_key_test_user: "{{ api_key_test_user_result.stdout | regex_search('[^/]+$') }}"
+
+ - name: Change default admin password for tests
+ command:
+ cmd: >
+ sensuctl user change-password admin
+ --current-password 'P@ssw0rd!'
+ --new-password insecure
+
+
+- name: Test against a new backend
+ hosts: has_api_key_support
+ collections:
+ - sensu.sensu_go
+ gather_facts: no
+
+ tasks:
+ - name: Fail with invalid password
+ mutator:
+ auth:
+ user: admin
+ password: not-a-password
+ url: http://localhost:8080
+ name: my_mutator
+ command: sensu-influxdb-mutator
+ timeout: 30
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Fail with bad username
+ asset_info:
+ auth:
+ user: not-a-valid-user
+ password: insecure
+ url: http://localhost:8080
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Fail with bad API token
+ hook:
+ auth:
+ api_key: not-a-valid-api-key
+ url: http://localhost:8080
+ name: restart_nginx
+ command: sudo systemctl start nginx
+ timeout: 60
+ stdin: false
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Fail with bad API token even if username/password would be OK
+ hook:
+ auth:
+ api_key: not-a-valid-api-key
+ user: admin
+ password: insecure
+ url: http://localhost:8080
+ name: restart_nginx
+ command: sudo systemctl start nginx
+ timeout: 60
+ stdin: false
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Authenticate with a valid user/pass combination
+ asset:
+ auth:
+ user: admin
+ password: insecure
+ url: http://localhost:8080
+ name: my_asset
+ builds:
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b
+ register: result
+
+ - assert:
+ that:
+ - result is success
+
+ - name: Authenticate with a valid API key
+ asset_info:
+ auth:
+ api_key: "{{ api_key_admin }}"
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result is success
+
+ - name: Authenticate with a valid API key and ignore user/password
+ asset_info:
+ auth:
+ api_key: "{{ api_key_admin }}"
+ user: not-a-valid-user
+ password: not-a-password
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result is success
+
+ - name: List assets using test user API key
+ asset_info:
+ auth:
+ api_key: "{{ api_key_test_user }}"
+
+ - name: Fail to create an asset because test_user can only list them
+ bonsai_asset:
+ auth:
+ api_key: "{{ api_key_test_user }}"
+ name: sensu/monitoring-plugins
+ version: 2.2.0-1
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+
+ - name: Fail to list entities because test_user has no access to entities
+ entity_info:
+ auth:
+ api_key: "{{ api_key_test_user }}"
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+
+
+- name: Prepare old backend
+ hosts: no_api_key_support
+ gather_facts: no
+
+ tasks:
+ - name: Configure sensuctl
+ command:
+ cmd: >
+ sensuctl configure
+ --non-interactive
+ --url http://localhost:8080
+ --username admin
+ --password P@ssw0rd!
+ --namespace default
+
+ - name: Change default admin password for tests
+ command:
+ cmd: >
+ sensuctl user change-password admin
+ --current-password 'P@ssw0rd!'
+ --new-password insecure
+
+
+- name: Test against an old backend
+ hosts: no_api_key_support
+ collections:
+ - sensu.sensu_go
+ gather_facts: no
+
+ tasks:
+ - name: Fail with invalid password
+ mutator:
+ auth:
+ user: admin
+ password: not-a-password
+ url: http://localhost:8080
+ name: my_mutator
+ command: sensu-influxdb-mutator
+ timeout: 30
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Fail with bad username
+ asset_info:
+ auth:
+ user: not-a-valid-user
+ password: insecure
+ url: http://localhost:8080
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Fail with API token
+ hook:
+ auth:
+ api_key: not-a-valid-api-key
+ url: http://localhost:8080
+ name: restart_nginx
+ command: sudo systemctl start nginx
+ timeout: 60
+ stdin: false
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Fail with bad API token even if username/password would be OK
+ hook:
+ auth:
+ api_key: not-a-valid-api-key
+ user: admin
+ password: insecure
+ url: http://localhost:8080
+ name: restart_nginx
+ command: sudo systemctl start nginx
+ timeout: 60
+ stdin: false
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Authentication' in result.msg"
+
+ - name: Authenticate with a valid user/pass combination
+ asset:
+ auth:
+ user: admin
+ password: insecure
+ url: http://localhost:8080
+ name: my_asset
+ builds:
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b
+ register: result
+
+ - assert:
+ that:
+ - result is success
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml
new file mode 100644
index 000000000..1648fc459
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_authentication/molecule.yml
@@ -0,0 +1,13 @@
+platforms:
+ - name: v6.4.1
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:6.4.1
+ groups: [ has_api_key_support ]
+ pre_build_image: true
+ pull: true
+ override_command: false
+ - name: v5.14.2
+ image: quay.io/xlab-steampunk/sensu-go-tests-sensu:5.14.2
+ groups: [ no_api_key_support ]
+ pre_build_image: true
+ pull: true
+ override_command: false
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml
new file mode 100644
index 000000000..a0867bb32
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/converge.yml
@@ -0,0 +1,67 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: no
+
+ tasks:
+ - name: Test connection using no verification
+ sensu.sensu_go.asset_info:
+ auth:
+ url: https://localhost:8080
+ verify: false
+
+ - name: Test connection using no verification (env var)
+ sensu.sensu_go.asset_info:
+ auth:
+ url: https://localhost:8080
+ environment:
+ SENSU_VERIFY: "false"
+
+ - name: Test connection using custom CA
+ sensu.sensu_go.user_info:
+ auth:
+ url: https://sensu-api:8080
+ ca_path: /etc/sensu/api-ca.crt
+
+ - name: Test connection using custom CA (env var)
+ sensu.sensu_go.user_info:
+ auth:
+ url: https://sensu-api:8080
+ environment:
+ SENSU_CA_PATH: /etc/sensu/api-ca.crt
+
+ - name: Test connection using default verification
+ sensu.sensu_go.asset_info:
+ auth:
+ url: https://localhost:8080
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - '"certificate" in result.msg'
+
+ - name: Test connection using custom CA not matching name
+ sensu.sensu_go.user_info:
+ auth:
+ url: https://localhost:8080
+ ca_path: /etc/sensu/api-ca.crt
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search("localhost.+sensu-api")
+
+ - name: Test connection using the wrong protocol
+ sensu.sensu_go.asset_info:
+ auth:
+ url: http://localhost:8080
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh
new file mode 100755
index 000000000..eb5509321
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Generate root CA certificate
+openssl genrsa -out sensu-api-ca.key 2048
+openssl req -x509 -sha256 -new -nodes -key sensu-api-ca.key -subj '/CN=Sensu-test CA' -days 1095 -out sensu-api-ca.crt
+
+# Generate certificate
+openssl genrsa -out sensu-api.key 2048
+openssl req -new -key "sensu-api.key" -out "sensu-api.csr" -sha256 -subj '/CN=sensu-api'
+openssl x509 -req -days 1095 -in "sensu-api.csr" -sha256 -CA "sensu-api-ca.crt" -CAkey "sensu-api-ca.key" -CAcreateserial -out "sensu-api.crt" -extfile "sensu-api.cnf" -extensions server
+
+# print content of certificate
+openssl x509 -in sensu-api-ca.crt -text
+openssl x509 -in sensu-api.crt -text \ No newline at end of file
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt
new file mode 100644
index 000000000..a2301e4b9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDETCCAfmgAwIBAgIUBSnmUh6PCEF3/6zTJ1DqH9rcNuswDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0yMjEyMTIwNjQ3NDdaFw0y
+NTEyMTEwNjQ3NDdaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCoAXA7t1aC4zNgZNCasPHxcyCfmC4J5zoh
+oTstyiFxhNv131EEQtpIABcWsgDXtQozJuAgHTVuGHEbHlFu7BFrh9Ik6FEznuZW
+pZ53V//LRtQZt7DLQrJP2ZJhGedSm+exZj8f0Uk8do0xI46BtwT/u5pzkLN8NLCk
+z+xu2mnFN+pdTqJLHP59JpRV1E0Al2E1m9rCfR8grGh4lTFDE/SwUueaHmg5Z0TN
+QuROuRii1HcK8g7QZ0HyOWRGfpNGzXq2kYUaGTvONrSwGe4bPjT/Jp4TElUV9mSA
+YoBE6JaDi1v28sXD9Cz6JgK63Ci0lmz6ny3H7kNfMQECgpj3KWL7AgMBAAGjUzBR
+MB0GA1UdDgQWBBQrUp514z1VOH+uWKjmtkvomGULLjAfBgNVHSMEGDAWgBQrUp51
+4z1VOH+uWKjmtkvomGULLjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4IBAQCg/Po64hN09w4QxwLvFRo050NBpvTi4JHrC/xQjFWl60UlQ+tCICtKDRRA
+MCpOt+PqJlivm8sDrSn86vqQKMJfQHGUDExNpcEWFG+QLUJ0aX5jFUj6+V+2hAbO
+g3QR9OvqeewuIfpHQJ8CTEbSamhc03LC2eX0AqdQUwSMuNrNgrZ0BBVTMkEfx8fM
+vfhWPjbO7WDjj0NJrh/FRXxIplvog1vGVY5RNRNtGgs3hGwh+roNq0kxOTO+Ye5D
+rtFSQ9CfjZI9qsT+XA8R6WzvFqSZSuxQpPkh70Pl6TPlK/W4Pqec2izZSlJxKFp1
+/rHU0nyXHJ2d7X5iV/sd3/WTs8kV
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf
new file mode 100644
index 000000000..1aec86d31
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.cnf
@@ -0,0 +1,9 @@
+[server]
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=issuer
+
+extendedKeyUsage=serverAuth
+keyUsage=digitalSignature, keyEncipherment
+subjectAltName=IP:127.0.0.1
+
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt
new file mode 100644
index 000000000..f2fdc951f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIUBvuM6lXj9IVDxOUs7Szg6+vzEu8wDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0yMjEyMTIwNzE1MzRaFw0y
+NTEyMTEwNzE1MzRaMBQxEjAQBgNVBAMMCXNlbnN1LWFwaTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAOHOCPfinziVMh0dJljYnnJ7/H6+Tj6fbf+I01U8
+hRQLhhKoA6n51uuThS6ncBiWhyC6t09i/SbXqvybdIVrn1Gcj/yyDpeTDxKs3GHY
+i7q3xeDkJ/qWwsBK7sGtN3F6T/qQUvnhMhG8DzsNH+G11G3PnOJD6CMwmsCzFqip
+GmagDFUOleHaMDaTZOiT6C8Bv+w08KakGuRoKtJUlIJCps9Za1ZnpcRKv5nY7Mxo
+K1/K1M9dDmznuh0nHkSVjBig5j3I3k4Fto4ApIN/eGHoyCc9uXHCHoB8K7xwGeNQ
+eNuxzYUmryemA51p/AjtIIXe7Hn9XxkTmWolCiv14Prp/ZUCAwEAAaOBnzCBnDAJ
+BgNVHRMEAjAAMB0GA1UdDgQWBBQ+DDirz38IyldwTg9p38Ti0wAytzA9BgNVHSME
+NjA0oRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENBghQFKeZSHo8IQXf/rNMn
+UOof2tw26zATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDwYDVR0R
+BAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAkXWRt3m4IGp/6+/icT5PCZ3M
+S7FCzx6f+2BxxvGdFX0JI1HTjqO7cUHQ57lC/eRHyx15W6CxgDkUCZJ/tJot0Q+k
+ZZPHUsO6mG+d/DdMyRbfzOtH0xSrIFZTXdG9gSfEGG+r5IX+TBcZ3BOHtIgRYJsK
+p3elglhsSgChxZ6SlcSp52KK9gaFcBywLtIt13BpRm/F9JX2IKmX4yZIFwvMg5W9
+MmDdR1GSBDe/uS9yRhst3hVOxBBLX9J2P1xBf4TYgmIAPqLhSCKIdr0ciRwwnV0D
+dOk8q2eCVLqgu+Hl/NJWqLvI99eRtya20uVV458ZMjVa1ugw4kcpU7UmOEZajg==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key
new file mode 100644
index 000000000..efea0f675
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA4c4I9+KfOJUyHR0mWNiecnv8fr5OPp9t/4jTVTyFFAuGEqgD
+qfnW65OFLqdwGJaHILq3T2L9Jteq/Jt0hWufUZyP/LIOl5MPEqzcYdiLurfF4OQn
++pbCwEruwa03cXpP+pBS+eEyEbwPOw0f4bXUbc+c4kPoIzCawLMWqKkaZqAMVQ6V
+4dowNpNk6JPoLwG/7DTwpqQa5Ggq0lSUgkKmz1lrVmelxEq/mdjszGgrX8rUz10O
+bOe6HSceRJWMGKDmPcjeTgW2jgCkg394YejIJz25ccIegHwrvHAZ41B427HNhSav
+J6YDnWn8CO0ghd7sef1fGROZaiUKK/Xg+un9lQIDAQABAoIBAQCEUbwnpoEvIx6O
+uPozrhyLceRwUQyA3eQTjhZpKGHDcU1LuXEMNf+fZH7y6+NgRTVCFKg+uP8nt9HW
+3THWzU47AMfPiHfMkryOcQVjwQWAkRg/xPM4gQf2rvJiRCLtOIONjO1SyIgSpGU4
+cWRxW5/0CWkhnjF2DZFhwpBQnWd/IsiiCrqCrEgKm4yjgu63LVEkBRmSwzkms2qO
+aWOBnut/DJNkMdV3Zaxb+MYkaYmrus6viPGk3brbt70qkuFzJh4l/KSZcCWHofUW
+3Rs1nrgNmrzaDEscULNZTE/4WSL32tyXyTo8ujIqVQCgMXbWc1QujV1AyHL1EZep
+KRSYpn6BAoGBAPkTc35qlOZSOBE/GeIddqFlNbNK0XaJhjH0lNc1L/aoxNLQCNqX
+cUCu1CB2YVoERhghvuLUOULkeVt9LeJywpLYW7l3lLbqJoBWedRqEHAkVxGxARqO
+KWwgMyFgphTrkgfmSlOYWrZ1hW8BPL84pwQbLgyRU6jzkmHi0Re/GbjZAoGBAOgU
++YgO47vqCfQhrinWYESsqH3OZGoAaIlRqEMv4vPIq3UHtWne1je9vGRCTCgdA+GJ
+6tkIbx04m61FvPXW/HlWlSYvA4zafWZFKjWkNtsrOZBIknqtQ9h5ca1CsQjEfWLJ
+WdMN5DMTNQeK6irNHFHXFJHejlig/dCOdwF7oFUdAoGAXsgbHBEc5mSFN3Lmu5fw
+q8wi2j5vZQdCTMJA3YA850Uj2QEXTW9xxmaBDHVf6GxV7BrzU8fknmLpF3qUOmbn
+ShARH4u0yMJjslS1+bH+3V3G0FGmFN/iPWYnbt0jdjSKlnz47cS9SE+CXlJ/Nlkt
+nS9mn5ux1UoS+zLf1ISBGkkCgYAZ3XyR9VbcMpE5bCeE8id4f+WnX8FBLxp3c2pV
+UpjwooS5XkqRqgwl2jeM1Pa34cP09vH9jjsT/qMbBJKys2sf+s5UmxjCMfeX1k2F
+/O9ALekUc4Ifuf+9uIs6zBv+5iczQ02HgBzWtGVMmebPLlzhoh7gwvUKW6bt/3Kz
+75tG4QKBgFz2h/AVPz52Fraztx7QUc2J9sgM4yERvZqVnVCYVINrczi7iV+LS+yE
+x4CenPWPRgnoh4P2jmqybmw+3rGsk4UyTPYZhvM7FUUI5DsD/Quk9KYgDIQBQTxV
+0ryPzC6j8uYweF9xLKwfv+fIj8j+iqcOwhXWd5+E2YOXOIdKS3Oa
+-----END RSA PRIVATE KEY-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem
new file mode 100644
index 000000000..beff18dce
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/files/sensu-api.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDWjCCAkKgAwIBAgIUAOVga11hbTPIxN+jFbDcB6YX/PwwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0yMjEyMDkxMzM5NThaFw0y
+NTEyMDgxMzM5NThaMBQxEjAQBgNVBAMMCXNlbnN1LWFwaTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAJGoCboPEiiKj9E9YUUdVR3oIS1mHYQLqqd0T0Y5
+BTp4XbeLdvv/oFF5nol5QUWUJm7PHa8CUY6LO9k+fW2VkhQcBnrrv0H1e23DgyWm
+J2pOjp54RYHciGErRnAkwpNt3Z6Q6oMnnK77hp5YiPsk3AvwgQaHFgOpI1L6Z2VI
+difGuckMUruwVKT0q+y2zHDvewzf55J+ykBKVYjVEi8Kx/gaAIe12VybAyg5P0QM
+ZUiKfcb7f/sae4KEZOWSXKfh8HaK/YXQN0JxwFujPt+MHxtUlzEyOakWgBjhP7nQ
+BB8FgQmyE+m9xuq2bHei/qzx1183Pwd+zPrVoAWY+Rlv9x8CAwEAAaOBnzCBnDAJ
+BgNVHRMEAjAAMB0GA1UdDgQWBBTqiIfWrKiwVZTFchGktuW8WWtFRDA9BgNVHSME
+NjA0oRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENBghQxv8cd6BODLyXX7rdB
+udWKli0pBjATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDwYDVR0R
+BAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEANptW8pIOrmulcqZ78n+b95zu
+zcxoD0n1QjhouiEqlCgEaVoOpWbm12tds6pALDVuBNF1lYuTe2Kdffxoz/q7lpxb
+Z07fU5lXF3mGa8CtsbXxlnpRxu1HIQwChp3EGxjxhOiGmfxfULicOl/z3nfWR9zu
++/7jpKRXAJ+O5JvxdU754cmXXftk+XncUz19ZbizX7trWDuqzkyTxD/0lNRA6OZN
+hVF8FVE/Mju3FXbY4atvptV4e8MOsz1vfS6piPtU9HzD2miWuoY4di+OgC3DDTQs
+4eDkOLvyoJ04JQMst928Oka6NPHnfUVxBeQg9JtzCDbFT/km4bQ4snybQ4vPnQ==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml
new file mode 100644
index 000000000..1785c689f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/molecule.yml
@@ -0,0 +1,20 @@
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - prepare
+ - converge
+ - destroy
+
+platforms:
+ - name: backend
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
+ override_command: false
+ privileged: true
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
+ etc_hosts:
+ # sensu-api is the hostname used in the test certificate
+ sensu-api: 127.0.0.1
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml
new file mode 100644
index 000000000..1f1463259
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/misc_api_cert/prepare.yml
@@ -0,0 +1,13 @@
+---
+- name: Prepare
+ hosts: all
+
+ tasks:
+ - name: Install backend with secured API
+ include_role:
+ name: sensu.sensu_go.backend
+ vars:
+ version: 5.21.0
+ api_cert_file: files/sensu-api.crt
+ api_key_file: files/sensu-api.key
+ api_trusted_ca_file: files/sensu-api-ca.crt
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml
new file mode 100644
index 000000000..0112074b9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/converge.yml
@@ -0,0 +1,360 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Fetch all AD auth providers and verify the presence
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Fail when trying to create a AD auth provider with missing required params
+ sensu.sensu_go.ad_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+ ignore_errors: true
+
+ - ansible.builtin.assert:
+ that:
+ - result is failed
+ - "'state is present but all of the following are missing' in result.msg"
+
+ - name: Create AD auth provider with minimal params (check mode)
+ sensu.sensu_go.ad_auth_provider: &create-provider
+ state: present
+ name: activedirectory
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'activedirectory'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+
+ - name: Make sure AD auth provider was not created when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create AD auth provider with minimal params
+ sensu.sensu_go.ad_auth_provider: *create-provider
+ register: result
+
+ - ansible.builtin.assert: *create-provider-assertions
+
+ - name: Make sure AD auth provider was created
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'activedirectory'
+
+ - name: Idempotence check for AD auth provider creation with minimal params
+ sensu.sensu_go.ad_auth_provider: *create-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update AD auth provider (check mode)
+ sensu.sensu_go.ad_auth_provider: &update-provider
+ state: present
+ name: activedirectory
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ groups_prefix: dev
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &update-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'activedirectory'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.object.groups_prefix == 'dev'
+
+ - name: Make sure AD auth provider was not updated in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'activedirectory'
+ - result.objects.0.servers.0.host == '127.0.0.1'
+ - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.groups_prefix == ''
+
+ - name: Update AD auth provider
+ sensu.sensu_go.ad_auth_provider: *update-provider
+ register: result
+
+ - ansible.builtin.assert: *update-provider-assertions
+
+ - name: Make sure AD auth provider was updated
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'activedirectory'
+ - result.objects.0.servers.0.host == '127.0.0.1'
+ - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.groups_prefix == 'dev'
+
+ - name: Idempotence check for AD auth provider modification
+ sensu.sensu_go.ad_auth_provider: *update-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Add AD auth provider server to existing one (check mode)
+ sensu.sensu_go.ad_auth_provider: &create-extra-provider
+ state: present
+ name: activedirectory
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ - host: 127.0.0.2
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ groups_prefix: dev
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-extra-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'activedirectory'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.1.host == '127.0.0.2'
+ - result.object.servers.1.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.1.user_search.base_dn == 'dc=acme,dc=org'
+
+ - name: Make sure extra AD auth provider was not created when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.servers | length == 1
+
+ - name: Create an extra AD auth provider
+ sensu.sensu_go.ad_auth_provider: *create-extra-provider
+ register: result
+
+ - ansible.builtin.assert: *create-extra-provider-assertions
+
+ - name: Make sure extra AD auth provider was created
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.servers | length == 2
+ - result.objects.0.metadata.name == 'activedirectory'
+
+ - name: Idempotence check for extra AD auth provider creation
+ sensu.sensu_go.ad_auth_provider: *create-extra-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create a AD auth provider with all params
+ sensu.sensu_go.ad_auth_provider: &create-provider-all-params
+ auth:
+ url: http://localhost:8080
+ state: present
+ name: other-activedirectory
+ servers:
+ - host: 127.0.0.1
+ port: 636
+ insecure: false
+ security: tls
+ trusted_ca_file: /path/to/trusted-certificate-authorities.pem
+ client_cert_file: /path/to/ssl/cert.pem
+ client_key_file: /path/to/ssl/key.pem
+ default_upn_domain: example.org
+ binding:
+ user_dn: cn=binder,dc=acme,dc=org
+ password: ad_password
+ group_search:
+ base_dn: dc=acme,dc=org
+ attribute: member
+ name_attribute: cn
+ object_class: groupOfNames
+ user_search:
+ base_dn: dc=acme,dc=org
+ attribute: uid
+ name_attribute: cn
+ object_class: person
+ groups_prefix: dev
+ username_prefix: ad
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'other-activedirectory'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.0.port == 636
+ - result.object.servers.0.insecure == false
+ - result.object.servers.0.security == 'tls'
+ - result.object.servers.0.trusted_ca_file == '/path/to/trusted-certificate-authorities.pem'
+ - result.object.servers.0.client_cert_file == '/path/to/ssl/cert.pem'
+ - result.object.servers.0.client_key_file == '/path/to/ssl/key.pem'
+ - result.object.servers.0.default_upn_domain == 'example.org'
+ - result.object.servers.0.binding.user_dn == 'cn=binder,dc=acme,dc=org'
+ - "'password' not in result.object.servers.0.binding"
+ - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.group_search.attribute == 'member'
+ - result.object.servers.0.group_search.name_attribute == 'cn'
+ - result.object.servers.0.group_search.object_class == 'groupOfNames'
+ - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.user_search.attribute == 'uid'
+ - result.object.servers.0.user_search.name_attribute == 'cn'
+ - result.object.servers.0.user_search.object_class == 'person'
+ - result.object.groups_prefix == 'dev'
+ - result.object.username_prefix == 'ad'
+
+ - name: Idempotence check for AD auth provider creation with all params
+ sensu.sensu_go.ad_auth_provider: *create-provider-all-params
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Delete AD auth provider (check mode)
+ sensu.sensu_go.ad_auth_provider: &delete-provider
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure AD auth provider was not deleted when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete AD auth provider
+ sensu.sensu_go.ad_auth_provider: *delete-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure AD auth provider was deleted
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: activedirectory
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Check if still any AD auth providers exist
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete AD auth provider
+ sensu.sensu_go.ad_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: other-activedirectory
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Delete a non-existent AD auth provider
+ sensu.sensu_go.ad_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: i-dont-exist
+ state: absent
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ad_auth_provider/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml
new file mode 100644
index 000000000..062ab7ee0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/converge.yml
@@ -0,0 +1,260 @@
+---
+- name: Setup sensuctl
+ hosts: all
+ gather_facts: no
+
+ tasks:
+ - name: Configure sensuctl
+ command:
+ cmd: >
+ sensuctl configure
+ --non-interactive
+ --url http://localhost:8080
+ --username admin
+ --password P@ssw0rd!
+ --namespace default
+
+
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create an asset with missing required parameters
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: builds'"
+
+ - name: Create an asset with empty builds
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ builds:
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'builds must include at least one element'"
+
+ - name: Create an asset with missing parameters for build
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ builds:
+ - url: http://assets.bonsai.sensu.io/asset
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'missing required arguments: sha512 found in builds'"
+
+ - name: Create an asset with minimal parameters
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: minimal_asset
+ builds:
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.builds | length == 1
+ - result.object.builds.0.url == 'https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz'
+ - result.object.builds.0.sha512 == '518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b'
+ - result.object.builds.0.filters == None
+ - result.object.builds.0.headers == None
+
+ - name: Create an asset
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ builds:
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b
+ filters:
+ - entity.system.os == 'linux'
+ - entity.system.arch == 'amd64'
+ - entity.system.platform == 'rhel'
+ headers:
+ Sensu-Blivet: foo
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_alpine_linux_amd64.tar.gz
+ sha512: b2da25ecd7642e6de41fde37d674fe19dcb6ee3d680e145e32289f7cfc352e6b5f9413ee9b701d61faeaa47b399aa30b25885dbc1ca432c4061c8823774c28f3
+ filters:
+ - entity.system.os == 'linux'
+ - entity.system.arch == 'amd64'
+ - entity.system.platform == 'alpine'
+ headers:
+ Sensu-Blivet: bar
+ annotations:
+ sensio.io.bonsai.url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sensio.io.bonsai.tier: Community
+ sensio.io.bonsai.version: 4.0.0
+ sensio.io.bonsai.tags: ruby-runtime-2.4.4
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'asset'
+ - result.object.builds | length == 2
+ - result.object.metadata.annotations | dict2items | length == 4
+
+ - name: Test asset creation idempotence
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ builds:
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_alpine_linux_amd64.tar.gz
+ sha512: b2da25ecd7642e6de41fde37d674fe19dcb6ee3d680e145e32289f7cfc352e6b5f9413ee9b701d61faeaa47b399aa30b25885dbc1ca432c4061c8823774c28f3
+ filters:
+ - entity.system.platform == 'alpine'
+ - entity.system.os == 'linux'
+ - entity.system.arch == 'amd64'
+ headers:
+ Sensu-Blivet: bar
+ - url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sha512: 518e7c17cf670393045bff4af318e1d35955bfde166e9ceec2b469109252f79043ed133241c4dc96501b6636a1ec5e008ea9ce055d1609865635d4f004d7187b
+ filters:
+ - entity.system.arch == 'amd64'
+ - entity.system.platform == 'rhel'
+ - entity.system.os == 'linux'
+ headers:
+ Sensu-Blivet: foo
+ annotations:
+ sensio.io.bonsai.url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sensio.io.bonsai.tier: Community
+ sensio.io.bonsai.version: 4.0.0
+ sensio.io.bonsai.tags: ruby-runtime-2.4.4
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify an asset
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ builds:
+ - url: https://assets.bonsai.sensu.io/73a6f8b6f56672630d83ec21676f9a6251094475/sensu-plugins-disk-checks_5.0.0_centos_linux_amd64.tar.gz
+ sha512: 0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf
+ annotations:
+ sensio.io.bonsai.url: https://assets.bonsai.sensu.io/68546e739d96fd695655b77b35b5aabfbabeb056/sensu-plugins-cpu-checks_4.0.0_centos_linux_amd64.tar.gz
+ sensio.io.bonsai.tier: Community
+ sensio.io.bonsai.version: 4.0.0
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.builds | length == 1
+ - result.object.builds.0.sha512 == '0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf'
+ - not result.object.builds.0.headers
+ - not result.object.builds.0.filters
+ - result.object.metadata.annotations | dict2items | length == 3
+ - "'sensu.io.bonsai.tags' not in result.object.metadata.annotations"
+
+ - name: Fetch a specific asset
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ name: asset
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'asset'
+
+ - name: Fetch all assets
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'asset'
+
+ - name: Delete an asset
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: asset2
+ state: absent
+
+ - name: Fetch all assets again after deletion
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'asset'
+
+ - name: Try to fetch non-existing asset
+ asset_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-asset
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
+
+ - name: Create an asset with a deprecated definition
+ shell:
+ cmd: |
+ cat <<EOF | sensuctl create
+ type: Asset
+ api_version: core/v2
+ metadata:
+ name: old_asset
+ namespace: default
+ spec:
+ url: https://example.com/sensu-cpu-check_0.0.3_linux_amd64.tar.gz
+ sha512: 0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf
+ filters:
+ - entity.system.os == 'linux'
+ - entity.system.arch == 'amd64'
+ EOF
+
+ - name: Update deprecated asset
+ asset:
+ auth:
+ url: http://localhost:8080
+ name: old_asset
+ builds:
+ - url: https://example.com/sensu-cpu-check_0.0.3_linux_amd64.tar.gz
+ sha512: 0ce9d52b270b77f4cab754e55732ae002228201d0bd01a89b954a0655b88c1ee6546e2f82cfd1eec04689af90ad940cde128e8867912d9e415f4a58d7fdcdadf
+ filters:
+ - entity.system.os == 'linux'
+ - entity.system.arch == 'amd64'
+ register: result
+
+ - assert:
+ that: result is changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_asset/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml
new file mode 100644
index 000000000..645ebe644
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/converge.yml
@@ -0,0 +1,230 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create a check with missing required parameters
+ check:
+ auth:
+ url: http://localhost:8080
+ name: minimal_check
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: subscriptions, command'"
+
+
+ - name: Create a check with minimal parameters
+ check:
+ auth:
+ url: http://localhost:8080
+ name: minimal_check
+ command: /bin/true
+ subscriptions:
+ - checks
+ - also_checks
+ interval: 30
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_check'
+
+ - name: Create a check with minimal parameters idempotence
+ check:
+ auth:
+ url: http://localhost:8080
+ name: minimal_check
+ command: /bin/true
+ subscriptions:
+ - checks
+ - also_checks
+ interval: 30
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create a check
+ check: &check
+ auth:
+ url: http://localhost:8080
+ name: check
+ command: /bin/true
+ subscriptions:
+ - checks
+ - also_checks
+ handlers:
+ - default
+ - not_default
+ interval: 30
+ publish: True
+ timeout: 30
+ ttl: 100
+ stdin: False
+ low_flap_threshold: 20
+ high_flap_threshold: 60
+ proxy_entity_name: switch-dc-01
+ proxy_requests:
+ entity_attributes: ['entity.entity_class == "proxy"']
+ splay: True
+ splay_coverage: 90
+ output_metric_format: nagios_perfdata
+ output_metric_handlers: ['influx-db']
+ round_robin: True
+ env_vars:
+ foo: bar
+ runtime_assets: awesomeness
+ secrets:
+ - name: test
+ secret: value
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'check'
+ - "result.object.secrets == [{'name': 'test', 'secret': 'value'}]"
+
+ - name: Test check creation idempotence
+ check: *check
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify a check
+ check:
+ auth:
+ url: http://localhost:8080
+ name: check
+ interval: 30
+ command: /bin/true
+ subscriptions:
+ - checks
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - not result.object.handlers
+ - not result.object.env_vars
+ - not result.object.runtime_assets
+ - "'also_checks' not in result.object.subscriptions"
+
+ - name: Create a second check
+ check:
+ auth:
+ url: http://localhost:8080
+ name: check2
+ interval: 30
+ command: /usr/bin/true
+ subscriptions: checks
+ handlers: default
+
+ - name: Fetch all checks
+ check_info:
+ auth:
+ url: http://localhost:8080
+
+ - name: Fetch a specific check
+ check_info:
+ auth:
+ url: http://localhost:8080
+ name: check
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'check'
+
+ - name: Delete a check
+ check:
+ auth:
+ url: http://localhost:8080
+ name: check
+ state: absent
+
+ - name: Get all checks
+ check_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'check2'
+
+ - name: Try to fetch non-existing check
+ check_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-check
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
+
+ - name: Create check for idempotency test of complex fields
+ check:
+ name: complex
+ command: sleep 10
+ interval: 10
+ subscriptions:
+ - sub1
+ - sub2
+ handlers: []
+ proxy_requests:
+ entity_attributes:
+ - "entity.entity_class == 'proxy'"
+ - "entity.entity_class == 'demo'"
+ check_hooks:
+ warning:
+ - h1
+ - h2
+ - h3
+ error:
+ - h4
+ - h2
+ env_vars:
+ var1: val1
+ var2: val2
+
+ - name: Test for idempotency test of complex fields
+ check:
+ name: complex
+ command: sleep 10
+ interval: 10
+ subscriptions:
+ - sub2
+ - sub1
+ runtime_assets: []
+ proxy_requests:
+ entity_attributes:
+ - "entity.entity_class == 'demo'"
+ - "entity.entity_class == 'proxy'"
+ check_hooks:
+ warning:
+ - h1
+ - h3
+ - h2
+ error:
+ - h2
+ - h4
+ env_vars:
+ var2: val2
+ var1: val1
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_check/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml
new file mode 100644
index 000000000..d92d56f85
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/converge.yml
@@ -0,0 +1,171 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: false
+ environment:
+ SENSU_ANSIBLE_DEBUG: "true"
+
+ tasks:
+ - name: Make sure we start clean
+ sensu.sensu_go.cluster_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is success
+ - result.objects | length == 0
+
+ - name: Create new cluster (check mode)
+ sensu.sensu_go.cluster: &cluster
+ name: alpha-cluster
+ api_urls: http://10.10.0.1:8080
+ check_mode: true
+ register: result
+ - ansible.builtin.assert: &cluster-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == "alpha-cluster"
+ - result.object.api_urls == ["http://10.10.0.1:8080"]
+
+ - name: Make sure things are still clean
+ sensu.sensu_go.cluster_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create new cluster
+ sensu.sensu_go.cluster: *cluster
+ register: result
+ - ansible.builtin.debug:
+ var: result
+ - ansible.builtin.assert: *cluster-assertions
+
+ - name: Make sure cluster is present on the backend
+ sensu.sensu_go.cluster_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "alpha-cluster"
+ - result.objects.0.api_urls == ["http://10.10.0.1:8080"]
+
+ - name: Create new cluster (idempotence)
+ sensu.sensu_go.cluster: *cluster
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update existing cluster (check mode)
+ sensu.sensu_go.cluster: &update
+ name: alpha-cluster
+ api_urls:
+ - http://10.10.0.2:8080
+ check_mode: true
+ register: result
+ - ansible.builtin.assert: &update-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == "alpha-cluster"
+ - result.object.api_urls == ["http://10.10.0.2:8080"]
+
+ - name: Make sure cluster did not change
+ sensu.sensu_go.cluster_info:
+ name: alpha-cluster
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.api_urls == ["http://10.10.0.1:8080"]
+
+ - name: Update existing cluster
+ sensu.sensu_go.cluster: *update
+ register: result
+ - ansible.builtin.assert: *update-assertions
+
+ - name: Make sure cluster is updated
+ sensu.sensu_go.cluster_info:
+ name: alpha-cluster
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "alpha-cluster"
+ - result.objects.0.api_urls == ["http://10.10.0.2:8080"]
+
+ - name: Update existing cluster (idempotence)
+ sensu.sensu_go.cluster: *update
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create additional cluster
+ sensu.sensu_go.cluster:
+ name: beta-cluster
+ api_urls:
+ - https://10.20.0.1:8080
+ - https://10.20.0.2:8080
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == "beta-cluster"
+ - result.object.api_urls == [
+ "https://10.20.0.1:8080",
+ "https://10.20.0.2:8080",
+ ]
+
+ - name: Make sure we have two replicators now
+ sensu.sensu_go.cluster_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Delete cluster (check mode)
+ sensu.sensu_go.cluster: &delete
+ name: alpha-cluster
+ state: absent
+ check_mode: true
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure we still have two replicators
+ sensu.sensu_go.cluster_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Delete cluster
+ sensu.sensu_go.cluster: *delete
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Now we have only one cluster
+ sensu.sensu_go.cluster_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "beta-cluster"
+
+ - name: And the first cluster is no more
+ sensu.sensu_go.cluster_info:
+ name: alpha-cluster
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Delete cluster (idempotency)
+ sensu.sensu_go.cluster: *delete
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml
new file mode 100644
index 000000000..b771c76d3
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/converge.yml
@@ -0,0 +1,219 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create a cluster role with missing required parameters
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: rules'"
+
+ - name: Create a cluster role with empty rules
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ rules: []
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: rules'"
+
+ - name: Create a cluster role with invalid rule verbs
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ rules:
+ - verbs:
+ - list
+ - do_something
+ resources:
+ - entities
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+
+ - name: Create a cluster role with minimal parameters
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_cluster_role
+ rules:
+ - verbs:
+ - get
+ - list
+ resources:
+ - entities
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_test_cluster_role'
+ - result.object.rules | length == 1
+ - result.object.rules.0.verbs | length == 2
+ - result.object.rules.0.verbs == ['get', 'list']
+ - result.object.rules.0.resources == ['entities']
+
+ - name: Check idempotence of cluster role creation with minimal parameters
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_cluster_role
+ rules:
+ - verbs:
+ - list
+ - get
+ resources:
+ - entities
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create a cluster role
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ rules:
+ - verbs:
+ - list
+ resources:
+ - assets
+ - checks
+ resource_names:
+ - some_resource_1
+ - some_resource_2
+ - verbs:
+ - list
+ - get
+ resources:
+ - checks
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_cluster_role'
+ - result.object.rules | length == 2
+ - result.object.rules.0.verbs | length == 1
+ - result.object.rules.0.verbs == ['list']
+ - result.object.rules.0.resources == ['assets', 'checks']
+ - result.object.rules.0.resource_names == ['some_resource_1', 'some_resource_2']
+ - result.object.rules.1.verbs == ['list', 'get']
+ - result.object.rules.1.resources == ['checks']
+ - not result.object.rules.1.resource_names
+
+ - name: Check idempotence of cluster role creation
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ rules:
+ - verbs:
+ - list
+ resources:
+ - checks
+ - assets
+ resource_names:
+ - some_resource_2
+ - some_resource_1
+ - verbs:
+ - get
+ - list
+ resources:
+ - checks
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Modify a cluster role
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ rules:
+ - verbs:
+ - list
+ resources:
+ - assets
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.rules | length == 1
+ - result.object.rules.0.verbs == ['list']
+ - result.object.rules.0.resources == ['assets']
+ - not result.object.rules.0.resource_names
+
+ - name: Fetch all cluster roles
+ cluster_role_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 8 # There are 6 default cluster roles
+
+ - name: Fetch a specific cluster role
+ cluster_role_info:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'test_cluster_role'
+
+ - name: Delete a cluster role
+ cluster_role:
+ auth:
+ url: http://localhost:8080
+ state: absent
+ name: minimal_test_cluster_role
+ register: result
+
+ - name: Fetch all cluster roles after deletion of a cluster role
+ cluster_role_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 7 # There are 6 default cluster roles
+
+ - name: Try to fetch non-existing role
+ cluster_role_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-role
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml
new file mode 100644
index 000000000..5fbb40def
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/converge.yml
@@ -0,0 +1,194 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create a cluster role binding with missing required parameters
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role_binding
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: cluster_role'"
+
+ - name: Create a cluster role binding without providing users or groups
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role_binding
+ cluster_role: test_cluster_role
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'missing required parameters: users or groups'"
+
+ - name: Create a cluster role binding with minimal parameters (users only)
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_cluster_role_binding_users
+ cluster_role: test_cluster_role
+ users:
+ - test_user
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_test_cluster_role_binding_users'
+ - result.object.role_ref.name == 'test_cluster_role'
+ - result.object.subjects | length == 1
+ - result.object.subjects.0.name == 'test_user'
+ - result.object.subjects.0.type == 'User'
+
+ - name: Create a cluster role binding with minimal parameters (groups only)
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_cluster_role_binding_groups
+ cluster_role: test_cluster_role
+ groups:
+ - test_group_1
+ - test_group_2
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_test_cluster_role_binding_groups'
+ - result.object.role_ref.name == 'test_cluster_role'
+ - result.object.subjects | length == 2
+
+ - name: Check idempotence of cluster role binding creation with minimal parameters
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_cluster_role_binding_groups
+ cluster_role: test_cluster_role
+ groups:
+ - test_group_2
+ - test_group_1
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create a cluster role binding
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role_binding
+ cluster_role: test_cluster_role
+ users:
+ - test_user_1
+ - test_user_2
+ groups:
+ - test_group_1
+ - test_group_2
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_cluster_role_binding'
+ - result.object.role_ref.name == 'test_cluster_role'
+ - result.object.subjects | length == 4
+
+ - name: Check idempotence of cluster role binding creation
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role_binding
+ cluster_role: test_cluster_role
+ users:
+ - test_user_2
+ - test_user_1
+ groups:
+ - test_group_2
+ - test_group_1
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Modify a cluster role binding
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role_binding
+ cluster_role: another_cluster_role
+ users:
+ groups:
+ - group_1
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_cluster_role_binding'
+ - result.object.role_ref.name == 'another_cluster_role'
+ - result.object.subjects | length == 1
+ - result.object.subjects.0.name == 'group_1'
+ - result.object.subjects.0.type == 'Group'
+
+ - name: Fetch all cluster role bindings
+ cluster_role_binding_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 6 # There are 3 pre-existing cluster role bindings by default
+
+ - name: Fetch a specific cluster role binding
+ cluster_role_binding_info:
+ auth:
+ url: http://localhost:8080
+ name: test_cluster_role_binding
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'test_cluster_role_binding'
+
+ - name: Delete a cluster role binding
+ cluster_role_binding:
+ auth:
+ url: http://localhost:8080
+ state: absent
+ name: test_cluster_role_binding
+ register: result
+
+ - name: Fetch all cluster roles bindings after deletion
+ cluster_role_binding_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 5 # There are 3 pre-existing cluster role bindings by default
+
+ - name: Try to fetch non-existing binding
+ cluster_role_binding_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-binding
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_cluster_role_binding/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml
new file mode 100644
index 000000000..a6963f973
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/converge.yml
@@ -0,0 +1,133 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Retrieve empty list of external datastores
+ datastore_info:
+ auth: &auth
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
+
+ - name: Make sure we fail creation if dsn parameter is missing
+ datastore:
+ auth: *auth
+ name: my-incomplete-datastore
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+
+ - name: Enable external datastore with minimal parameters
+ datastore: &idempotence
+ auth: *auth
+ name: my-datastore
+ dsn: postgresql://user:secret@host:port/dbname
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == "my-datastore"
+ - result.object.dsn == "postgresql://user:secret@host:port/dbname"
+
+ - name: Check for idempotence
+ datastore: *idempotence
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Try to add another external storage
+ datastore:
+ auth: *auth
+ name: my-second-datastore
+ dsn: postgresql://user:secret@host:port/db
+ pool_size: 123
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+
+ - name: Update external datastore
+ datastore:
+ auth: *auth
+ name: my-datastore
+ dsn: postgresql://user:secret@host:port/new
+ pool_size: 321
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == "my-datastore"
+ - result.object.dsn == "postgresql://user:secret@host:port/new"
+ - result.object.pool_size == 321
+
+ - name: Fetch all datastores
+ datastore_info:
+ auth: *auth
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Fetch a specific datastore
+ datastore_info:
+ auth: *auth
+ name: my-datastore
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "my-datastore"
+ - result.objects.0.dsn == "postgresql://user:secret@host:port/new"
+ - result.objects.0.pool_size == 321
+
+ - name: Remove external datastore
+ datastore:
+ auth: *auth
+ name: my-datastore
+ state: absent
+
+ - name: Re-fetch all datastores
+ datastore_info:
+ auth: *auth
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Try to fetch non-existing datastore
+ datastore_info:
+ auth: *auth
+ name: my-fictional-datastore
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
+
+ - name: Try to remove non-existing external datastore
+ datastore:
+ auth: *auth
+ name: my-fictional-datastore
+ state: absent
+
+ - assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_datastore/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml
new file mode 100644
index 000000000..4cca68495
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/converge.yml
@@ -0,0 +1,191 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create entity with minimal parameters
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: minimal_entity
+ entity_class: proxy
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.entity_class == 'proxy'
+ - result.object.metadata.name == 'minimal_entity'
+
+ - name: Create entity with minimal parameters idempotence
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: minimal_entity
+ entity_class: proxy
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create entity
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: entity
+ entity_class: proxy
+ subscriptions:
+ - web
+ - prod
+ system:
+ hostname: playbook-entity
+ os: linux
+ platform: ubutntu
+ network:
+ interfaces:
+ - name: lo
+ addresses:
+ - 127.0.0.1/8
+ - ::1/128
+ - name: eth0
+ mac: 52:54:00:20:1b:3c
+ addresses:
+ - 93.184.216.34/24
+ last_seen: 1522798317
+ deregister: yes
+ deregistration_handler: email-handler
+ redact:
+ - password
+ - pass
+ - api_key
+ user: agent
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.entity_class == 'proxy'
+ - result.object.subscriptions == ['web', 'prod']
+ - result.object.system.network.interfaces.0.name == 'lo'
+ - result.object.system.network.interfaces.1.mac == '52:54:00:20:1b:3c'
+ - result.object.last_seen == 1522798317
+ - result.object.deregister == True
+ - result.object.deregistration.handler == 'email-handler'
+ - result.object.redact == ['password', 'pass', 'api_key']
+ - result.object.user == 'agent'
+ - result.object.metadata.name == 'entity'
+
+ - name: Test entity creation idempotence
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: entity
+ entity_class: proxy
+ subscriptions:
+ - web
+ - prod
+ system:
+ hostname: playbook-entity
+ os: linux
+ platform: ubutntu
+ network:
+ interfaces:
+ - name: lo
+ addresses:
+ - 127.0.0.1/8
+ - ::1/128
+ - name: eth0
+ mac: 52:54:00:20:1b:3c
+ addresses:
+ - 93.184.216.34/24
+ last_seen: 1522798317
+ deregister: yes
+ deregistration_handler: email-handler
+ redact:
+ - password
+ - pass
+ - api_key
+ user: agent
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify entity
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: entity
+ entity_class: some_class
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.entity_class == 'some_class'
+ - "'deprecations' in result"
+
+ - name: Create an agent entity
+ entity: &agent_entity
+ auth:
+ url: http://localhost:8080
+ name: entity2
+ entity_class: agent
+
+ - name: Create an agent entity (idempotence)
+ entity: *agent_entity
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Fetch all entities
+ entity_info:
+ auth:
+ url: http://localhost:8080
+
+ - name: Fetch a specific entity
+ entity_info:
+ auth:
+ url: http://localhost:8080
+ name: entity
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'entity'
+ - result.objects.0.entity_class == 'some_class'
+
+ - name: Delete entity
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: entity
+ state: absent
+
+ - name: Fetch all entities
+ entity_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'entity2'
+
+ - name: Try to fetch non-existing entity
+ entity_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-entity
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_entity/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml
new file mode 100644
index 000000000..4b87df9e2
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/converge.yml
@@ -0,0 +1,181 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: false
+
+ tasks:
+ - name: Make sure we start clean
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is success
+ - result.objects | length == 0
+
+ - name: Create new replicator (check mode)
+ sensu.sensu_go.etcd_replicator: &replicator
+ name: cluster-role-replicator
+ ca_cert: /etc/sensu/certs/ca.pem
+ cert: /etc/sensu/certs/cert.pem
+ key: /etc/sensu/certs/key.pem
+ url: https://sensu.alpha.example.com:2379
+ resource: ClusterRole
+ check_mode: true
+ register: result
+ - ansible.builtin.assert: &replicator-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == "cluster-role-replicator"
+ - result.object.resource == "ClusterRole"
+ - result.object.insecure is false
+ - result.object.cert == "/etc/sensu/certs/cert.pem"
+
+ - name: Make sure things are still clean
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create new replicator
+ sensu.sensu_go.etcd_replicator: *replicator
+ register: result
+ - ansible.builtin.debug:
+ var: result
+ - ansible.builtin.assert: *replicator-assertions
+
+ - name: Make sure replicator is present on the backend
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "cluster-role-replicator"
+ - result.objects.0.resource == "ClusterRole"
+ - result.objects.0.insecure is false
+ - result.objects.0.cert == "/etc/sensu/certs/cert.pem"
+
+ - name: Create new replicator (idempotence)
+ sensu.sensu_go.etcd_replicator: *replicator
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update existing replicator (check mode)
+ sensu.sensu_go.etcd_replicator: &update
+ name: cluster-role-replicator
+ insecure: true
+ url:
+ - http://sensu.alpha.example.com:2379
+ resource: ClusterRole
+ check_mode: true
+ register: result
+ - ansible.builtin.assert: &update-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == "cluster-role-replicator"
+ - result.object.resource == "ClusterRole"
+ - result.object.insecure is true
+
+ - name: Make sure replicator did not change
+ sensu.sensu_go.etcd_replicator_info:
+ name: cluster-role-replicator
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.insecure is false
+
+ - name: Update existing replicator
+ sensu.sensu_go.etcd_replicator: *update
+ register: result
+ - ansible.builtin.assert: *update-assertions
+
+ - name: Make sure replicator is updated
+ sensu.sensu_go.etcd_replicator_info:
+ name: cluster-role-replicator
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "cluster-role-replicator"
+ - result.objects.0.resource == "ClusterRole"
+ - result.objects.0.insecure is true
+
+ - name: Update existing replicator (idempotence)
+ sensu.sensu_go.etcd_replicator: *update
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create additional replicator
+ sensu.sensu_go.etcd_replicator:
+ name: new-replicator
+ insecure: true
+ url: http://dummy.url
+ resource: Role
+ namespace: default
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == "new-replicator"
+ - result.object.insecure is true
+ - result.object.resource == "Role"
+ - result.object.namespace == "default"
+
+ - name: Make sure we have two replicators now
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Delete replicator (check mode)
+ sensu.sensu_go.etcd_replicator: &delete
+ name: cluster-role-replicator
+ state: absent
+ check_mode: true
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure we still have two replicators
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Delete replicator
+ sensu.sensu_go.etcd_replicator: *delete
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Now we have only one replicator
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == "new-replicator"
+
+ - name: And the cluster role replicator is no more
+ sensu.sensu_go.etcd_replicator_info:
+ name: cluster-role-replicator
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Delete replicator (idempotency)
+ sensu.sensu_go.etcd_replicator: *delete
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_etcd_replicator/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml
new file mode 100644
index 000000000..885698e1e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/converge.yml
@@ -0,0 +1,217 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Event info without entity
+ event_info:
+ auth:
+ url: http://localhost:8080
+ check: simple-check
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == \"missing parameter(s) required by 'check': entity\""
+
+ - name: Create event with missing objects on remote
+ event:
+ auth:
+ url: http://localhost:8080
+ timestamp: 134532453
+ entity: entity
+ check: check
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == "Entity with name 'entity' does not exist on remote."
+
+ - name: Create simple entity
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: awesome_entity
+ entity_class: proxy
+
+ - name: Create event with missing check on remote
+ event:
+ auth:
+ url: http://localhost:8080
+ timestamp: 134532453
+ entity: awesome_entity
+ check: check
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg == "Check with name 'check' does not exist on remote."
+
+ - name: Create a simple check
+ check:
+ auth:
+ url: http://localhost:8080
+ name: awesome_check
+ command: /bin/true
+ subscriptions:
+ - checks
+ - also_checks
+ interval: 30
+
+ - name: Get non-existing last event for entity and check combo
+ event_info:
+ auth:
+ url: http://localhost:8080
+ entity: awesome_entity
+ check: awesome_check
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
+
+ - name: Create event with minimal parameters
+ event:
+ auth:
+ url: http://localhost:8080
+ entity: awesome_entity
+ check: awesome_check
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.check.metadata.name == 'awesome_check'
+ - result.object.check.command == '/bin/true'
+ - result.object.entity.metadata.name == 'awesome_entity'
+
+ - name: Get last event
+ event_info:
+ auth:
+ url: http://localhost:8080
+ entity: awesome_entity
+ check: awesome_check
+ register: result
+
+ - assert:
+ that:
+ - result.objects.0.check.metadata.name == 'awesome_check'
+ - result.objects.0.check.command == '/bin/true'
+ - result.objects.0.entity.metadata.name == 'awesome_entity'
+
+ - name: Get all events
+ event_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Create second entity
+ entity:
+ auth:
+ url: http://localhost:8080
+ name: entity2
+ entity_class: proxy
+
+ - name: Create a second check
+ check:
+ auth:
+ url: http://localhost:8080
+ name: check2
+ command: /bin/false
+ subscriptions:
+ - checks
+ - also_checks
+ interval: 30
+
+ - name: Create event with all parameters
+ event:
+ auth:
+ url: http://localhost:8080
+ entity: entity2
+ check: check2
+ check_attributes:
+ duration: 1.945
+ executed: 1522100915
+ history:
+ - executed: 1552505193
+ status: 1
+ - executed: 1552505293
+ status: 0
+ - executed: 1552505393
+ status: 0
+ - executed: 1552505493
+ status: 0
+ issued: 1552506034
+ last_ok: 1552506033
+ output: '10'
+ state: 'passing'
+ status: 'ok'
+ total_state_change: 0
+ metric_attributes:
+ handlers:
+ - handler1
+ - handler2
+ points:
+ - name: "sensu-go-sandbox.curl_timings.time_total"
+ tags: # Both value of the pairs must be strings
+ - name: "response_time_in_ms"
+ value: "101"
+ timestamp: 1552506033
+ value: 0.005
+ - name: "sensu-go-sandbox.curl_timings.time_namelookup"
+ tags:
+ - name: "namelookup_time_in_ms"
+ value: "57"
+ timestamp: 1552506033
+ value: 0.004
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.entity.metadata.name == 'entity2'
+ - result.object.check.metadata.name == 'check2'
+ - result.object.check.command == '/bin/false'
+ - result.object.check.duration == 1.945
+ - result.object.check.executed == 1522100915
+ - "result.object.check.history.0 == {'executed': 1552505193, 'status': 1}"
+ - result.object.check.issued == 1552506034
+ - result.object.check.last_ok == 1552506033
+ - result.object.check.output == '10'
+ - result.object.check.state == 'passing'
+ - result.object.check.status == 0
+ - result.object.check.total_state_change == 0
+
+ - name: Get events matching entity2
+ event_info:
+ auth:
+ url: http://localhost:8080
+ entity: entity2
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.check.metadata.name == 'check2'
+
+ - name: Get all events
+ event_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_event/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml
new file mode 100644
index 000000000..6ae4f03a2
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/converge.yml
@@ -0,0 +1,150 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create filter with missing required parameters
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: filter
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: action, expressions'"
+
+ - name: Create filter with minimal parameters
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: minimal_filter
+ action: allow
+ expressions:
+ - event.check.occurrences == 1
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.action == 'allow'
+ - result.object.expressions == ['event.check.occurrences == 1']
+ - result.object.metadata.name == 'minimal_filter'
+
+ - name: Create filter with minimal parameters idempotence
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: minimal_filter
+ action: allow
+ expressions:
+ - event.check.occurences == 1
+ register: result
+
+ - name: Create a filter
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: filter
+ action: deny
+ expressions:
+ - event.check.interval == 10
+ - event.check.occurrences == 1
+ runtime_assets: awesomeness
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.action == 'deny'
+ - result.object.expressions == ['event.check.interval == 10', 'event.check.occurrences == 1']
+ - result.object.runtime_assets == ['awesomeness']
+ - result.object.metadata.name == 'filter'
+
+ - name: Test filter creation idempotence
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: filter
+ action: deny
+ expressions:
+ - event.check.interval == 10
+ - event.check.occurrences == 1
+ runtime_assets: awesomeness
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create a second filter
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: filter2
+ action: allow
+ expressions: event.check.interval == 10
+
+ - name: Fetch all filters
+ filter_info:
+ auth:
+ url: http://localhost:8080
+
+ - name: Fetch a specific filter
+ filter_info:
+ auth:
+ url: http://localhost:8080
+ name: filter
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'filter'
+
+ - name: Modify a filter
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: filter
+ action: deny
+ expressions: event.check.interval > 10
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.expressions == ['event.check.interval > 10']
+ - not result.object.runtime_assets
+
+ - name: Delete a filter
+ filter:
+ auth:
+ url: http://localhost:8080
+ name: filter
+ state: absent
+
+ - name: Get all filters
+ filter_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'filter2'
+
+ - name: Try to fetch non-existing filter
+ filter_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-filter
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_filter/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml
new file mode 100644
index 000000000..5a71e7e9e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/converge.yml
@@ -0,0 +1,109 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create handler set
+ handler_set:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ handlers:
+ - udp_handler
+ - tcp_handler
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.handlers == ['udp_handler', 'tcp_handler']
+ - result.object.metadata.name == 'handler'
+
+ - name: Test handler set creation idempotence
+ handler_set:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ handlers:
+ - udp_handler
+ - tcp_handler
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify handler set
+ handler_set:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ handlers:
+ - slack
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.handlers == ['slack']
+
+ - name: Fetch all set handlers
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Fetch a specific handler set
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'handler'
+
+ - name: Delete handler set
+ handler_set:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ state: absent
+
+ - name: Get the Sensu Go backend's version
+ uri:
+ url: http://localhost:8080/version
+ return_content: true
+ register: backend_version
+
+ - name: Get all handlers set
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+ # Sensu Go 5.15.0 returns none when no handler sets are present
+ when: (backend_version.content | from_json).sensu_backend != '5.15.0'
+
+ - assert:
+ that:
+ - result.objects | length == 0
+ # Sensu Go 5.15.0 returns none when no handler sets are present
+ when: (backend_version.content | from_json).sensu_backend != '5.15.0'
+
+ - name: Try to fetch non-existing handler
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-handler
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_handler_set/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml
new file mode 100644
index 000000000..160c27d62
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/converge.yml
@@ -0,0 +1,139 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create hook with minimal parameters
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: minimal_hook
+ command: /bin/true
+ timeout: 10
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.command == '/bin/true'
+ - result.object.metadata.name == 'minimal_hook'
+
+ - name: Create hook with minimal parameters idempotence
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: minimal_hook
+ command: /bin/true
+ timeout: 10
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create a hook
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: hook
+ command: /bin/true
+ timeout: 30
+ stdin: yes
+ runtime_assets:
+ - ruby2.4
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.command == '/bin/true'
+ - result.object.timeout == 30
+ - result.object.stdin == True
+ - result.object.runtime_assets == ['ruby2.4']
+ - result.object.metadata.name == 'hook'
+
+ - name: Test hook creation idempotence
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: hook
+ command: /bin/true
+ timeout: 30
+ stdin: yes
+ runtime_assets:
+ - ruby2.4
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify a hook
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: hook
+ command: python -c "print('Modified.')"
+ timeout: 60
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - not result.object.stdin
+ - not result.object.runtime_assets
+
+ - name: Create a second hook
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: hook2
+ command: sensu-influxdb-hook
+ timeout: 30
+
+ - name: Fetch all hooks
+ hook_info:
+ auth:
+ url: http://localhost:8080
+
+ - name: Fetch a specific hook
+ hook_info:
+ auth:
+ url: http://localhost:8080
+ name: hook
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'hook'
+
+ - name: Delete a hook
+ hook:
+ auth:
+ url: http://localhost:8080
+ name: hook
+ state: absent
+
+ - name: Fetch all hooks
+ hook_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'hook2'
+
+ - name: Try to fetch non-existing hook
+ hook_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-hook
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_hook/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml
new file mode 100644
index 000000000..053bdf82d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/converge.yml
@@ -0,0 +1,358 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Fetch all LDAP auth providers and verify the presence
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Fail when trying to create a LDAP auth provider with missing required params
+ sensu.sensu_go.ldap_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+ ignore_errors: true
+
+ - ansible.builtin.assert:
+ that:
+ - result is failed
+ - "'state is present but all of the following are missing' in result.msg"
+
+ - name: Create LDAP auth provider with minimal params (check mode)
+ sensu.sensu_go.ldap_auth_provider: &create-provider
+ state: present
+ name: openldap
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'openldap'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+
+ - name: Make sure LDAP auth provider was not created when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create LDAP auth provider with minimal params
+ sensu.sensu_go.ldap_auth_provider: *create-provider
+ register: result
+
+ - ansible.builtin.assert: *create-provider-assertions
+
+ - name: Make sure LDAP auth provider was created
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'openldap'
+
+ - name: Idempotence check for LDAP auth provider creation with minimal params
+ sensu.sensu_go.ldap_auth_provider: *create-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update LDAP auth provider (check mode)
+ sensu.sensu_go.ldap_auth_provider: &update-provider
+ state: present
+ name: openldap
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ groups_prefix: dev
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &update-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'openldap'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.object.groups_prefix == 'dev'
+
+ - name: Make sure LDAP auth provider was not updated in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'openldap'
+ - result.objects.0.servers.0.host == '127.0.0.1'
+ - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.groups_prefix == ''
+
+ - name: Update LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider: *update-provider
+ register: result
+
+ - ansible.builtin.assert: *update-provider-assertions
+
+ - name: Make sure LDAP auth provider was updated
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'openldap'
+ - result.objects.0.servers.0.host == '127.0.0.1'
+ - result.objects.0.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.objects.0.groups_prefix == 'dev'
+
+ - name: Idempotence check for LDAP auth provider modification
+ sensu.sensu_go.ldap_auth_provider: *update-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Add LDAP auth provider server to existing one (check mode)
+ sensu.sensu_go.ldap_auth_provider: &create-extra-provider
+ state: present
+ name: openldap
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ - host: 127.0.0.2
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+ groups_prefix: dev
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-extra-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'openldap'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.1.host == '127.0.0.2'
+ - result.object.servers.1.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.1.user_search.base_dn == 'dc=acme,dc=org'
+
+ - name: Make sure extra LDAP auth provider was not created when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.servers | length == 1
+
+ - name: Create an extra LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider: *create-extra-provider
+ register: result
+
+ - ansible.builtin.assert: *create-extra-provider-assertions
+
+ - name: Make sure extra LDAP auth provider was created
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.servers | length == 2
+ - result.objects.0.metadata.name == 'openldap'
+
+ - name: Idempotence check for extra LDAP auth provider creation
+ sensu.sensu_go.ldap_auth_provider: *create-extra-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create a LDAP auth provider with all params
+ sensu.sensu_go.ldap_auth_provider: &create-provider-all-params
+ auth:
+ url: http://localhost:8080
+ state: present
+ name: other-openldap
+ servers:
+ - host: 127.0.0.1
+ port: 636
+ insecure: false
+ security: tls
+ trusted_ca_file: /path/to/trusted-certificate-authorities.pem
+ client_cert_file: /path/to/ssl/cert.pem
+ client_key_file: /path/to/ssl/key.pem
+ binding:
+ user_dn: cn=binder,dc=acme,dc=org
+ password: ldap_password
+ group_search:
+ base_dn: dc=acme,dc=org
+ attribute: member
+ name_attribute: cn
+ object_class: groupOfNames
+ user_search:
+ base_dn: dc=acme,dc=org
+ attribute: uid
+ name_attribute: cn
+ object_class: person
+ groups_prefix: dev
+ username_prefix: ldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'other-openldap'
+ - result.object.servers.0.host == '127.0.0.1'
+ - result.object.servers.0.port == 636
+ - result.object.servers.0.insecure == false
+ - result.object.servers.0.security == 'tls'
+ - result.object.servers.0.trusted_ca_file == '/path/to/trusted-certificate-authorities.pem'
+ - result.object.servers.0.client_cert_file == '/path/to/ssl/cert.pem'
+ - result.object.servers.0.client_key_file == '/path/to/ssl/key.pem'
+ - result.object.servers.0.binding.user_dn == 'cn=binder,dc=acme,dc=org'
+ - "'password' not in result.object.servers.0.binding"
+ - result.object.servers.0.group_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.group_search.attribute == 'member'
+ - result.object.servers.0.group_search.name_attribute == 'cn'
+ - result.object.servers.0.group_search.object_class == 'groupOfNames'
+ - result.object.servers.0.user_search.base_dn == 'dc=acme,dc=org'
+ - result.object.servers.0.user_search.attribute == 'uid'
+ - result.object.servers.0.user_search.name_attribute == 'cn'
+ - result.object.servers.0.user_search.object_class == 'person'
+ - result.object.groups_prefix == 'dev'
+ - result.object.username_prefix == 'ldap'
+
+ - name: Idempotence check for LDAP auth provider creation with all params
+ sensu.sensu_go.ldap_auth_provider: *create-provider-all-params
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Delete LDAP auth provider (check mode)
+ sensu.sensu_go.ldap_auth_provider: &delete-provider
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure LDAP auth provider was not deleted when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider: *delete-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure LDAP auth provider was deleted
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: openldap
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Check if still any LDAP auth providers exist
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: other-openldap
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Delete a non-existent LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: i-dont-exist
+ state: absent
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_ldap_auth_provider/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml
new file mode 100644
index 000000000..52db18f85
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/converge.yml
@@ -0,0 +1,144 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create mutator with minimal parameters
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: minimal_mutator
+ command: /bin/true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.command == '/bin/true'
+ - result.object.metadata.name == 'minimal_mutator'
+
+ - name: Create mutator with minimal parameters idempotence
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: minimal_mutator
+ command: /bin/true
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create a mutator
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: mutator
+ command: sensu-influxdb-mutator
+ timeout: 30
+ env_vars:
+ INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086
+ INFLUXDB_USER: sensu
+ runtime_assets:
+ - sensu-influxdb-mutator
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.command == 'sensu-influxdb-mutator'
+ - result.object.timeout == 30
+ - result.object.runtime_assets == ['sensu-influxdb-mutator']
+ - result.object.metadata.name == 'mutator'
+
+ - name: Test mutator creation idempotence
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: mutator
+ command: sensu-influxdb-mutator
+ timeout: 30
+ env_vars:
+ INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086
+ INFLUXDB_USER: sensu
+ runtime_assets:
+ - sensu-influxdb-mutator
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify a mutator
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: mutator
+ command: sensu-influxdb-mutator
+ timeout: 60
+ secrets:
+ - name: test
+ secret: value
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - not result.object.env_vars
+ - not result.object.runtime_assets
+ - "result.object.secrets == [{'name': 'test', 'secret': 'value'}]"
+
+ - name: Create a second mutator
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: mutator2
+ command: sensu-influxdb-mutator
+ timeout: 30
+
+ - name: Fetch all mutators
+ mutator_info:
+ auth:
+ url: http://localhost:8080
+
+ - name: Fetch a specific mutator
+ mutator_info:
+ auth:
+ url: http://localhost:8080
+ name: mutator
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'mutator'
+
+ - name: Delete a mutator
+ mutator:
+ auth:
+ url: http://localhost:8080
+ name: mutator
+ state: absent
+
+ - name: Fetch all mutators
+ mutator_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+ - result.objects.0.metadata.name == 'minimal_mutator'
+
+ - name: Try to fetch non-existing mutator
+ mutator_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-mutator
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_mutator/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml
new file mode 100644
index 000000000..e7f332e2f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/converge.yml
@@ -0,0 +1,78 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Fetch all namespaces
+ namespace_info:
+ auth:
+ url: http://localhost:8080
+ register: default_state
+
+ - name: store starting length
+ set_fact:
+ starting_length: "{{ default_state.objects | length }}"
+
+ - name: Create namespace
+ namespace:
+ auth:
+ url: http://localhost:8080
+ name: dev
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.name == 'dev'
+
+ - name: Create namespace idempotence
+ namespace:
+ auth:
+ url: http://localhost:8080
+ name: dev
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create a second namespace
+ namespace:
+ auth:
+ url: http://localhost:8080
+ name: production
+
+ - name: Fetch all namespaces
+ namespace_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == expected_length | int
+ - result.objects.1.name == 'dev'
+ vars:
+ expected_length: "{{ starting_length|int + 2 }}"
+
+ - name: Delete namespace
+ namespace:
+ auth:
+ url: http://localhost:8080
+ name: dev
+ state: absent
+
+ - name: Fetch all namespaces
+ namespace_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == expected_length | int
+ - result.objects.1.name == 'production'
+ vars:
+ expected_length: "{{ starting_length|int + 1 }}"
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_namespace/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml
new file mode 100644
index 000000000..89f42f48a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/converge.yml
@@ -0,0 +1,276 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Fetch all auth providers and verify the presence
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Fail when trying to create a OIDC auth provider with missing required params
+ sensu.sensu_go.oidc_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ register: result
+ ignore_errors: true
+
+ - ansible.builtin.assert:
+ that:
+ - result is failed
+ - "'state is present but all of the following are missing' in result.msg"
+
+ - name: Create OIDC auth provider with minimal params (check mode)
+ sensu.sensu_go.oidc_auth_provider: &create-provider
+ state: present
+ name: oidc_name
+ client_id: a8e43af034e7f2608780
+ client_secret: b63968394be6ed2edb61c93847ee792f31bf6216
+ server: https://oidc.example.com:9031
+ username_claim: email
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'oidc_name'
+ - result.object.client_id == 'a8e43af034e7f2608780'
+ - "'client_secret' not in result.object"
+ - result.object.server == 'https://oidc.example.com:9031'
+ - result.object.username_claim == 'email'
+
+ - name: Make sure OIDC auth provider was not created when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create OIDC auth provider with minimal params
+ sensu.sensu_go.oidc_auth_provider: *create-provider
+ register: result
+
+ - ansible.builtin.assert: *create-provider-assertions
+
+ - name: Make sure OIDC auth provider was created
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'oidc_name'
+
+ - name: Idempotence check for OIDC auth provider creation with minimal params
+ sensu.sensu_go.oidc_auth_provider: *create-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update OIDC auth provider (check mode)
+ sensu.sensu_go.oidc_auth_provider: &update-provider
+ state: present
+ name: oidc_name
+ additional_scopes:
+ - groups
+ - email
+ - username
+ client_id: a8e43af034e7f2608780
+ client_secret: b63968394be6ed2edb61c93847ee792f31bf6216
+ server: https://oidc.example.com:9031
+ username_claim: email
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &update-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'oidc_name'
+ - result.object.additional_scopes == ['groups', 'email', 'username']
+ - result.object.client_id == 'a8e43af034e7f2608780'
+ - "'client_secret' not in result.object"
+ - result.object.server == 'https://oidc.example.com:9031'
+ - result.object.username_claim == 'email'
+
+ - name: Make sure OIDC auth provider was not updated in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'oidc_name'
+ - result.objects.0.additional_scopes == ['openid']
+ - result.objects.0.client_id == 'a8e43af034e7f2608780'
+ - "'client_secret' not in result.objects.0"
+ - result.objects.0.server == 'https://oidc.example.com:9031'
+ - result.objects.0.username_claim == 'email'
+
+ - name: Update OIDC auth provider
+ sensu.sensu_go.oidc_auth_provider: *update-provider
+ register: result
+
+ - ansible.builtin.assert: *update-provider-assertions
+
+ - name: Make sure OIDC auth provider was updated
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'oidc_name'
+ - result.objects.0.additional_scopes == ['groups', 'email', 'username']
+ - result.objects.0.client_id == 'a8e43af034e7f2608780'
+ - "'client_secret' not in result.objects.0"
+ - result.objects.0.server == 'https://oidc.example.com:9031'
+ - result.objects.0.username_claim == 'email'
+
+ - name: Idempotence check for OIDC auth provider modification
+ sensu.sensu_go.oidc_auth_provider: *update-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create a OIDC auth provider with all params
+ sensu.sensu_go.oidc_auth_provider: &create-provider-all-params
+ auth:
+ url: http://localhost:8080
+ state: present
+ name: other-oidc_name
+ additional_scopes:
+ - groups
+ - email
+ - username
+ client_id: a8e43af034e7f2608780
+ client_secret: b63968394be6ed2edb61c93847ee792f31bf6216
+ disable_offline_access: false
+ redirect_uri: http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback
+ server: https://oidc.example.com:9031
+ groups_claim: groups
+ groups_prefix: 'oidc:'
+ username_claim: email
+ username_prefix: 'oidc:'
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'other-oidc_name'
+ - result.object.additional_scopes == ['groups', 'email', 'username']
+ - result.object.client_id == 'a8e43af034e7f2608780'
+ - "'client_secret' not in result.object"
+ - result.object.disable_offline_access == false
+ - result.object.redirect_uri == 'http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback'
+ - result.object.server == 'https://oidc.example.com:9031'
+ - result.object.groups_claim == 'groups'
+ - result.object.groups_prefix == 'oidc:'
+ - result.object.username_claim == 'email'
+ - result.object.username_prefix == 'oidc:'
+
+ - name: Idempotence check for OIDC auth provider creation with all params
+ sensu.sensu_go.oidc_auth_provider: *create-provider-all-params
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Delete OIDC auth provider (check mode)
+ sensu.sensu_go.oidc_auth_provider: &delete-provider
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure OIDC auth provider was not deleted when running in check mode
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete OIDC auth provider
+ sensu.sensu_go.oidc_auth_provider: *delete-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure OIDC auth provider was deleted
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: oidc_name
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Check if still any OIDC auth providers exist
+ sensu.sensu_go.auth_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete OIDC auth provider
+ sensu.sensu_go.oidc_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: other-oidc_name
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Delete a non-existent OIDC auth provider
+ sensu.sensu_go.oidc_auth_provider:
+ auth:
+ url: http://localhost:8080
+ name: i-dont-exist
+ state: absent
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_oidc_auth_provider/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml
new file mode 100644
index 000000000..8fe9f0682
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/converge.yml
@@ -0,0 +1,129 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create pipe handler with minimal parameters
+ pipe_handler:
+ auth:
+ url: http://localhost:8080
+ name: minimal_handler
+ command: sensu-influxdb-handler
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.command == 'sensu-influxdb-handler'
+ - result.object.metadata.name == 'minimal_handler'
+
+ - name: Create pipe handler with minimal parameters idempotence
+ pipe_handler:
+ auth:
+ url: http://localhost:8080
+ name: minimal_handler
+ command: sensu-influxdb-handler
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create pipe handler
+ pipe_handler: &handler
+ auth:
+ url: http://localhost:8080
+ name: handler
+ command: sensu-influxdb-handler
+ mutator: mutate_input
+ timeout: 30
+ filters:
+ - occurances
+ - production
+ env_vars:
+ INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086
+ INFLUXDB_USER: sensu
+ INFLUXDB_PASS: password
+ runtime_assets:
+ - sensu-influxdb-handler
+ secrets:
+ - name: test
+ secret: value
+ register: result
+
+ - debug:
+ msg: "{{result}}"
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.command == 'sensu-influxdb-handler'
+ - result.object.mutator == 'mutate_input'
+ - result.object.timeout == 30
+ - result.object.filters == ['occurances', 'production']
+ - result.object.runtime_assets == ['sensu-influxdb-handler']
+ - result.object.metadata.name == 'handler'
+ - "result.object.secrets == [{'name': 'test', 'secret': 'value'}]"
+
+ - name: Test pipe handler creation idempotence
+ pipe_handler: *handler
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify pipe handler
+ pipe_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ command: sensu-influxdb-handler
+ timeout: 60
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - not result.object.env_vars
+ - not result.object.runtime_assets
+
+ - name: Fetch all pipe handlers
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Fetch a specific pipe handler
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'handler'
+
+ - name: Delete pipe handler
+ pipe_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ state: absent
+
+ - name: Get all pipe handlers
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'minimal_handler'
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_pipe_handler/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml
new file mode 100644
index 000000000..b1871e70e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/converge.yml
@@ -0,0 +1,220 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create a role with missing required parameters
+ role:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: rules'"
+
+ - name: Create a role with empty rules
+ role:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ rules: []
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: rules'"
+
+ - name: Create a role with invalid rule verbs
+ role:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ rules:
+ - verbs:
+ - list
+ - do_something
+ resources:
+ - entities
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+
+ - name: Create a role with minimal parameters
+ role:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_role
+ rules:
+ - verbs:
+ - get
+ - list
+ resources:
+ - entities
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_test_role'
+ - result.object.rules | length == 1
+ - result.object.rules.0.verbs | length == 2
+ - result.object.rules.0.verbs == ['get', 'list']
+ - result.object.rules.0.resources == ['entities']
+
+ - name: Check idempotence of role creation with minimal parameters
+ role:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_role
+ rules:
+ - verbs:
+ - list
+ - get
+ resources:
+ - entities
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create a role
+ role:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ rules:
+ - verbs:
+ - list
+ resources:
+ - assets
+ - checks
+ resource_names:
+ - some_resource_1
+ - some_resource_2
+ - verbs:
+ - list
+ - get
+ resources:
+ - checks
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_role'
+ - result.object.rules | length == 2
+ - result.object.rules.0.verbs | length == 1
+ - result.object.rules.0.verbs == ['list']
+ - result.object.rules.0.resources == ['assets', 'checks']
+ - result.object.rules.0.resource_names == ['some_resource_1', 'some_resource_2']
+ - result.object.rules.1.verbs == ['list', 'get']
+ - result.object.rules.1.resources == ['checks']
+ - not result.object.rules.1.resource_names
+
+ - name: Check idempotence of role creation
+ role:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ rules:
+ - verbs:
+ - list
+ resources:
+ - checks
+ - assets
+ resource_names:
+ - some_resource_2
+ - some_resource_1
+ - verbs:
+ - get
+ - list
+ resources:
+ - checks
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Modify a role
+ role:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ rules:
+ - verbs:
+ - list
+ resources:
+ - assets
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.rules | length == 1
+ - result.object.rules.0.verbs == ['list']
+ - result.object.rules.0.resources == ['assets']
+ - not result.object.rules.0.resource_names
+
+ - name: Fetch all roles
+ role_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ # We created two roles but there might be others present by default
+ - result.objects | length >= 2
+
+ - name: Fetch a specific role
+ role_info:
+ auth:
+ url: http://localhost:8080
+ name: test_role
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'test_role'
+
+ - name: Delete a role
+ role:
+ auth:
+ url: http://localhost:8080
+ state: absent
+ name: minimal_test_role
+ register: result
+
+ - name: Fetch all roles after deletion of a role
+ role_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length >= 1
+
+ - name: Try to fetch non-existing role
+ role_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-role
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml
new file mode 100644
index 000000000..25f796769
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/converge.yml
@@ -0,0 +1,230 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create a role binding with missing required parameters
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but any of the following are missing: role, cluster_role'"
+
+ - name: Create a role binding with mutually exclusive role and cluster_role
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ role: test_role
+ cluster_role: test_cluster_role
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'parameters are mutually exclusive: role|cluster_role'"
+
+ - name: Create a role binding without providing users or groups
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ role: test_role
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'missing required parameters: users or groups'"
+
+ - name: Create a role binding with minimal parameters (users only)
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_role_binding_users
+ role: test_role
+ users:
+ - test_user
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_test_role_binding_users'
+ - result.object.role_ref.name == 'test_role'
+ - result.object.subjects | length == 1
+ - result.object.subjects.0.name == 'test_user'
+ - result.object.subjects.0.type == 'User'
+
+ - name: Create a role binding with minimal parameters (groups only)
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_role_binding_groups
+ role: test_role
+ groups:
+ - test_group_1
+ - test_group_2
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'minimal_test_role_binding_groups'
+ - result.object.role_ref.name == 'test_role'
+ - result.object.subjects | length == 2
+
+ - name: Check idempotence of role binding creation with minimal parameters
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: minimal_test_role_binding_groups
+ role: test_role
+ groups:
+ - test_group_2
+ - test_group_1
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create a role binding
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ role: test_role
+ users:
+ - test_user_1
+ - test_user_2
+ groups:
+ - test_group_1
+ - test_group_2
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_role_binding'
+ - result.object.role_ref.name == 'test_role'
+ - result.object.subjects | length == 4
+
+ - name: Check idempotence of role binding creation
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ role: test_role
+ users:
+ - test_user_2
+ - test_user_1
+ groups:
+ - test_group_2
+ - test_group_1
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Modify a role binding
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ cluster_role: test_cluster_role
+ users:
+ groups:
+ - group_1
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_role_binding'
+ - result.object.role_ref.name == 'test_cluster_role'
+ - result.object.role_ref.type == 'ClusterRole'
+ - result.object.subjects | length == 1
+ - result.object.subjects.0.name == 'group_1'
+ - result.object.subjects.0.type == 'Group'
+
+ - name: Create a role binding with cluster role
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding_with_cluster_role
+ cluster_role: test_cluster_role
+ users:
+ - user_1
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'test_role_binding_with_cluster_role'
+ - result.object.role_ref.name == 'test_cluster_role'
+ - result.object.role_ref.type == 'ClusterRole'
+ - result.object.subjects | length == 1
+ - result.object.subjects.0.name == 'user_1'
+ - result.object.subjects.0.type == 'User'
+
+ - name: Fetch all role bindings
+ role_binding_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length >= 4
+
+ - name: Fetch a specific role binding
+ role_binding_info:
+ auth:
+ url: http://localhost:8080
+ name: test_role_binding
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'test_role_binding'
+
+ - name: Delete a role binding
+ role_binding:
+ auth:
+ url: http://localhost:8080
+ state: absent
+ name: test_role_binding
+ register: result
+
+ - name: Fetch all roles bindings after deletion
+ role_binding_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length >= 3
+
+ - name: Try to fetch non-existing binding
+ role_binding_info:
+ auth:
+ url: http://localhost:8080
+ name: bad-bad-binding
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_role_binding/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml
new file mode 100644
index 000000000..cbb02fee5
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/converge.yml
@@ -0,0 +1,171 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: false
+
+ tasks:
+ - name: Make sure we start clean
+ sensu.sensu_go.secret_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is success
+ - result.objects | length == 0
+
+ - name: Create new secret (check mode)
+ sensu.sensu_go.secret: &secret
+ name: sample-secret
+ provider: env
+ id: MY_ENV_VAR
+ check_mode: true
+ register: result
+ - ansible.builtin.assert: &secret-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'sample-secret'
+ - result.object.id == 'MY_ENV_VAR'
+ - result.object.provider == 'env'
+
+ - name: Make sure things are still clean
+ sensu.sensu_go.secret_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create new secret
+ sensu.sensu_go.secret: *secret
+ register: result
+ - ansible.builtin.assert: *secret-assertions
+
+ - name: Make sure secret is present on the backend
+ sensu.sensu_go.secret_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'sample-secret'
+ - result.objects.0.id == 'MY_ENV_VAR'
+ - result.objects.0.provider == 'env'
+
+ - name: Create new secret (idempotence)
+ sensu.sensu_go.secret: *secret
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update existing secret (check mode)
+ sensu.sensu_go.secret: &update
+ name: sample-secret
+ id: MY_NEW_ENV_VAR
+ provider: env
+ check_mode: true
+ register: result
+ - ansible.builtin.assert: &update-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'sample-secret'
+ - result.object.id == 'MY_NEW_ENV_VAR'
+ - result.object.provider == 'env'
+
+ - name: Make sure secret did not change
+ sensu.sensu_go.secret_info:
+ name: sample-secret
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'sample-secret'
+ - result.objects.0.id == 'MY_ENV_VAR'
+ - result.objects.0.provider == 'env'
+
+ - name: Update existing secret
+ sensu.sensu_go.secret: *update
+ register: result
+ - ansible.builtin.assert: *update-assertions
+
+ - name: Make sure secret is updated
+ sensu.sensu_go.secret_info:
+ name: sample-secret
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'sample-secret'
+ - result.objects.0.id == 'MY_NEW_ENV_VAR'
+ - result.objects.0.provider == 'env'
+
+ - name: Update existing secret (idempotence)
+ sensu.sensu_go.secret: *update
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create additional secret
+ sensu.sensu_go.secret:
+ name: additional-secret
+ provider: hashi-vault
+ id: secret/database#pass
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'additional-secret'
+ - result.object.id == 'secret/database#pass'
+ - result.object.provider == 'hashi-vault'
+
+ - name: Make sure we have two secrets now
+ sensu.sensu_go.secret_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Delete secret (check mode)
+ sensu.sensu_go.secret: &delete
+ name: sample-secret
+ state: absent
+ check_mode: true
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure we still have two secrets
+ sensu.sensu_go.secret_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Delete secret
+ sensu.sensu_go.secret: *delete
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Now we have only one secret
+ sensu.sensu_go.secret_info:
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'additional-secret'
+
+ - name: And the sample-secret is no more
+ sensu.sensu_go.secret_info:
+ name: sample-secret
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Delete secret (idempotency)
+ sensu.sensu_go.secret: *delete
+ register: result
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secret/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml
new file mode 100644
index 000000000..db34cca9b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/converge.yml
@@ -0,0 +1,127 @@
+---
+- name: Converge
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Fetch all secrets providers and verify the presence of the
+ default env provider
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1 # env provider is present by default
+ - result.objects.0.metadata.name == 'env'
+
+ - name: Check idempotence of env provider creation
+ sensu.sensu_go.secrets_provider_env: &create-provider
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Delete the default env secrets provider (check mode)
+ sensu.sensu_go.secrets_provider_env: &delete-provider
+ auth:
+ url: http://localhost:8080
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure we didn't delete the env secrets provider in check mode
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete the default env secrets provider
+ sensu.sensu_go.secrets_provider_env: *delete-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure env secrets provider was deleted
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: env
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Delete a non-existent env secrets provider
+ sensu.sensu_go.secrets_provider_env:
+ auth:
+ url: http://localhost:8080
+ state: absent
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Fetch a non-existent secrets provider
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: env
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Re-create the env secrets provider (check mode)
+ sensu.sensu_go.secrets_provider_env: *create-provider
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'env'
+
+ - name: Make sure no secrets providers were created when running in check mode
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Re-create the env secrets provider
+ sensu.sensu_go.secrets_provider_env: *create-provider
+ register: result
+
+ - ansible.builtin.assert: *create-provider-assertions
+
+ - name: Make sure env secrets provider was really created
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: env
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'env'
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_env/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml
new file mode 100644
index 000000000..a5971afc4
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/converge.yml
@@ -0,0 +1,272 @@
+---
+- name: Pre-converge step - ensure presence of valid PEM files for TLS configuration
+ hosts: all
+ gather_facts: no
+ tasks:
+ # As part of configuring TLS for the vault secrets provider, we need
+ # to specify paths to CA cert and client cert/key files.
+ # If these files do not exist at the specified paths on SensuGo backend,
+ # or are not valid PEM files, the API returns error 500.
+ - name: Copy PEM files to Sensu backend
+ copy:
+ src: "files/{{ item }}"
+ dest: "/tmp/{{ item }}"
+ mode: 0744
+ loop:
+ - ca.crt
+ - client.crt
+ - client.key
+
+- name: Converge
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Fetch all secrets providers and verify the presence of the
+ default env provider
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1 # env provider is present by default
+ - result.objects.0.metadata.name == 'env'
+
+ - name: Fail when trying to create a vault provider with missing required params
+ sensu.sensu_go.secrets_provider_vault:
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ register: result
+ ignore_errors: true
+
+ - ansible.builtin.assert:
+ that:
+ - result is failed
+ - "'state is present but all of the following are missing' in result.msg"
+
+ - name: Create a vault provider with minimal params (check mode)
+ sensu.sensu_go.secrets_provider_vault: &create-provider
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ address: https://my-vault.com
+ token: VAULT_TOKEN
+ version: v1
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &create-provider-assertions
+ that:
+ - result is changed
+ - result.object.metadata.name == 'my-vault'
+ - result.object.client.address == 'https://my-vault.com'
+ - result.object.client.version == 'v1'
+
+ - name: Make sure vault provider was not created when running in check mode
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'env'
+
+ - name: Create a vault provider with minimal params
+ sensu.sensu_go.secrets_provider_vault: *create-provider
+ register: result
+
+ - ansible.builtin.assert: *create-provider-assertions
+
+ - name: Make sure vault secrets provider was created
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'my-vault'
+
+ - name: Idempotence check for vault provider creation with minimal params
+ sensu.sensu_go.secrets_provider_vault: *create-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Update vault secrets provider (check mode)
+ sensu.sensu_go.secrets_provider_vault: &update-provider
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ address: https://my-new-vault.com
+ token: ANOTHER_VAULT_TOKEN
+ version: v2
+ timeout: 5
+ max_retries: 1
+ rate_limit: 15.3
+ burst_limit: 50
+ tls:
+ ca_cert: /tmp/ca.crt
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert: &update-provider-assertions
+ that:
+ - result is changed
+ - result.object.client.address == 'https://my-new-vault.com'
+ - result.object.client.version == 'v2'
+ - result.object.client.timeout == '5s'
+ - result.object.client.max_retries == 1
+ - result.object.client.rate_limiter.limit == 15.3
+ - result.object.client.rate_limiter.burst == 50
+ - result.object.client.tls.ca_cert == '/tmp/ca.crt'
+
+ - name: Make sure vault secrets provider was not updated in check mode
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.metadata.name == 'my-vault'
+ - result.objects.0.client.address == 'https://my-vault.com'
+ - result.objects.0.client.version == 'v1'
+ - result.objects.0.client.tls == None
+
+ - name: Update vault secrets provider
+ sensu.sensu_go.secrets_provider_vault: *update-provider
+ register: result
+
+ - ansible.builtin.assert: *update-provider-assertions
+
+ - name: Make sure vault secrets provider was updated
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects.0.client.address == 'https://my-new-vault.com'
+ - result.objects.0.client.version == 'v2'
+ - result.objects.0.client.timeout == '5s'
+ - result.objects.0.client.max_retries == 1
+ - result.objects.0.client.rate_limiter.limit == 15.3
+ - result.objects.0.client.rate_limiter.burst == 50
+ - result.objects.0.client.tls.ca_cert == '/tmp/ca.crt'
+
+ - name: Idempotence check for vault provider modification
+ sensu.sensu_go.secrets_provider_vault: *update-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Create a vault secrets provider with all params
+ sensu.sensu_go.secrets_provider_vault: &create-provider-all-params
+ auth:
+ url: http://localhost:8080
+ name: my-other-vault
+ address: https://my-other-vault.com
+ token: OTHER_VAULT_TOKEN
+ version: v1
+ timeout: 30
+ max_retries: 1
+ rate_limit: 5.2
+ burst_limit: 90
+ tls:
+ ca_cert: /tmp/ca.crt
+ client_cert: /tmp/client.crt
+ client_key: /tmp/client.key
+ cname: my-vault.com
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'my-other-vault'
+ - result.object.client.address == 'https://my-other-vault.com'
+ - result.object.client.version == 'v1'
+ - result.object.client.timeout == '30s'
+ - result.object.client.max_retries == 1
+ - result.object.client.rate_limiter.limit == 5.2
+ - result.object.client.rate_limiter.burst == 90
+ - result.object.client.tls.ca_cert == '/tmp/ca.crt'
+ - result.object.client.tls.client_cert == '/tmp/client.crt'
+ - result.object.client.tls.client_key == '/tmp/client.key'
+ - result.object.client.tls.cname == 'my-vault.com'
+
+ - name: Idempotence check for vault provider creation with all params
+ sensu.sensu_go.secrets_provider_vault: *create-provider-all-params
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
+
+ - name: Delete a vault secrets provider (check mode)
+ sensu.sensu_go.secrets_provider_vault: &delete-provider
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ state: absent
+ check_mode: true
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure vault secrets provider was not deleted when running in check mode
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects | length == 1
+
+ - name: Delete a vault secrets provider
+ sensu.sensu_go.secrets_provider_vault: *delete-provider
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is changed
+
+ - name: Make sure vault secrets provider was deleted
+ sensu.sensu_go.secrets_provider_info:
+ auth:
+ url: http://localhost:8080
+ name: my-vault
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result.objects == []
+
+ - name: Delete a non-existent vault secrets provider
+ sensu.sensu_go.secrets_provider_vault:
+ auth:
+ url: http://localhost:8080
+ name: i-dont-exist
+ state: absent
+ register: result
+
+ - ansible.builtin.assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt
new file mode 100644
index 000000000..ceffe5219
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/ca.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCDCCAfCgAwIBAgITMgYuhrafZc78Bk5PXip8xwQqTjANBgkqhkiG9w0BAQsF
+ADAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjAxMDA3MDYzMzQzWhcNNDAwMTI2
+MDYzMzQzWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCk8LHitgJipeUaqWW2dsMe44TiT6XO17pPNJwRLFA7eSEN
+Vn5gCNd2yfEjsx7lXaZApghwz0YIE6gzkvFPS5cHCbPuBqyI6rSUPEXvMdkt7EGG
+40uhcX/otp0FvQr+Uvqo9NQyavTrgEPTudUptLFJd8QkZaVzprx061K2uV8Kjc4h
+0MDIX5+4amb+h4eEBloTjH4lc6INe2uezAodiRjUp/TOYJzr5OIPYd3jjbbB1G3T
+kwsQEz9qiEEwHN8AUGEFWqIy2O3BNTbS4D5Fv4n3U+HSFQdB5ORHGKbu/s0e8Tr8
+K5udPjM+ld8PazD2UIBBpWDbAqYvUrdTxYhxFqIrAgMBAAGjUzBRMB0GA1UdDgQW
+BBS6M9F187X6KhlndKvdTNxC25xRrzAfBgNVHSMEGDAWgBS6M9F187X6KhlndKvd
+TNxC25xRrzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAZvMa4
+R+AqR3lR3zpzFtN6OK/oFbUTloOE6+sWkFlE197YlvaUK0NmD67Xlvuor++KfCci
+ru2nOhUkqYRHQ5a/ZACEOhv0PyCcTTAsEWcjs/I/zBTxvzJUjX7o6X1wtVmo5lI5
+lLpru+5h5XsqHMnIbdqfGKJup3j44d0qUDUrVKAsS8ioWvmuyZyxczeKvb+CvQLq
+sNPSD+7H8l6WdWfo0unmCz7FkZDtSg9LCjzMF0TEe5D9ApWb6GtLy1XRKzg8jGzG
+MuuzNzlaqBgWI88HRL25ZkVLwNahb7dkkt82d8ghdmtkisXHm24BHwPnXOpQ2LGn
+ZPUwbq0OkVDt9nkr
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt
new file mode 100644
index 000000000..b6c5fe9fc
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.crt
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDazCCAlOgAwIBAgIUbhKhIYBliY9SvDegW9ND5+YAQSowDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEwMDYwOTMwMjZaFw0zMDEw
+MDQwOTMwMjZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDApjHtQI+w5Lu2uoMMo+iAcVb+6PjzWVL1G34d31HK
+hZynEPbCkJzpXsUOnbPagWmTXFMBblqgE1Pj4zT97oqWPBlIV3UA5M7rBQel76Vq
+YPKNDcZ8fGtCaL3wFFmN3ou6QaI+Zy3CJAy6Q71426xRawHWCgF7in3k90bVw3Qy
+hrW8VrEgcwobdHn79W9idx+OBYcku5Fn1ciZn9RQoL0GRIRsg5IyB0Uy9W1Vyc74
+Bp2LTySn4QwPflfCjb9+83oeMTwPTDU3RHPVbMVoL87+jXD5TeZTCNooIYDRYjP6
+3gWEQMVL+LJpaFZvdp26uArJnCu69wgyvpzGqnExKgl3AgMBAAGjUzBRMB0GA1Ud
+DgQWBBRBlAIaZUvLzoGXdIfOXulVTGIg0zAfBgNVHSMEGDAWgBRBlAIaZUvLzoGX
+dIfOXulVTGIg0zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBG
+U/Xnzm5+H6gXzq8hvFBIeaMYQmTgLwKt0w7yHBSQSNJC4qouIPgbyKKwrQJOvdI3
+/q3GRTkzMB/GKHRwh9nE4Z9wBLLY8b5K2t2yyQjpNtHyKeO+cc3E1gKSa0/QMEHh
+OLtQsCixfACb3U0KizhwocACQ52NVMd2LhBuO4UCAGi1I5SFZzrgIcrG9WoB1Qzx
+YoWh/49BL45LuLQ9MvPxya6ZuE0Pg38xRr+5yaEbnEAinVGy5eL+PNa5aMPogUB3
++hINK/Ous3ab0zJyJ+1lN/9ijx43R2RfCo8GXA7x9Wz+N+nW8MAJbICiJR8pljiy
+GFQiUdAX24flMYN14ZjX
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key
new file mode 100644
index 000000000..d22f68713
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/files/client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDApjHtQI+w5Lu2
+uoMMo+iAcVb+6PjzWVL1G34d31HKhZynEPbCkJzpXsUOnbPagWmTXFMBblqgE1Pj
+4zT97oqWPBlIV3UA5M7rBQel76VqYPKNDcZ8fGtCaL3wFFmN3ou6QaI+Zy3CJAy6
+Q71426xRawHWCgF7in3k90bVw3QyhrW8VrEgcwobdHn79W9idx+OBYcku5Fn1ciZ
+n9RQoL0GRIRsg5IyB0Uy9W1Vyc74Bp2LTySn4QwPflfCjb9+83oeMTwPTDU3RHPV
+bMVoL87+jXD5TeZTCNooIYDRYjP63gWEQMVL+LJpaFZvdp26uArJnCu69wgyvpzG
+qnExKgl3AgMBAAECggEAeP9pWEQ2e7oOFESszqGcBCArrcsRoXY23m+4FHcQ3gxx
+SZUkByvhAcpeJkHyloi1kLJqB/oRvXymMfmgbOUH8jgpAQC9IiSTuZTdKuGLmXbu
+oJ1ITyOuEnXK5iFB5hMi24chqeQQH2GZBNxLAr+mSTTWYGLEb52aWNbejKBIOPq9
+xnHD3gkFwod5dQt9ZY0bh+jTdM6cRuVyUooxbkFIkblHDQ8vEWsdC+b4rAXVH71e
+JjrnI4y/4zDdF5akexpaHrva+J1Y2PCkdFvfO4drl5XKysSiUMX37XN32LxU5/ON
+c6umKQnot1GL4ognOQw5HcygkT2Y8ltVbdcEK21xYQKBgQD7e1J48DYvpOhVVMaY
+fqV2RuXKpoyy+7RIU+YrxaduwJYqH6ZWnLOYf2ZtlKRVsuHw+4fjAxw4DW6FzkMA
+hXcgFwbLQO97dcxETPcq7Hny7mSQy8PO8iLXMHqOerxo16DNivc6Kw5NV+S/5XaU
+IMNCmNl+1ZzkdNhUjeknV5Sf/wKBgQDEHEaKXAQoxGffRFoZK0aj3FX8N+qsYn4c
+mftCkWZzKLMvwgjt0CXsHqrx9C3WecF2yrTGyMEsuI6eOBWVLqfkTUH9JgcjeFiF
+R2c25zBS28WZtZwFaIi3oUtXHX2rKyWza3rQZSqEgXXV+ck/Ks7LFqpuHmttnYHt
+qTXNIx+WiQKBgBpsXMAETU04QIkmvS8sr2n8DQz77vCnbcvjtN2IiQ0kAyMt7CZR
+lLVDPZnp8lJm10KgyyhZHU/uaVx5zaRyYY/nm3kju4X3XJ0YkSfbbPzPe5WTM2G9
+I1gE6fuqfb1uWqD+JvffqkMKJyjajVkHED0hHkkrXK7McCaCOqs9koo1AoGBAIEN
+NmJgUSBetxgWi8/aSZ8VJMRYK6cLHYBG2DCjLC8GDnyUDHoqqnnqaIXWML/d2bEJ
+jdLuUyjRvpBhydolHLjBGnazKqltzZrPNR3NH2C3XR5cg3KPqsBkdQa70nHsb9/V
+D7nJiSQvaVLJEGTwD6tXnAnhHMLCjrjNzCjVPzk5AoGBANHxGdKUaZivQV3dMo3p
+6WHfwe1rTdaiahvEHH2wi4ch+m7b92p+ey2aPg8kvnHKh8TG0NuAcJZ1/lG2qex8
+89pmdwaqkggi/ApPR0vvZiIQgSawlPW3pg1VAW/DtzYGAJlGWwbI212szN94MDYH
+t31cQ5V1qUo1rJaQE0rM0OwB
+-----END PRIVATE KEY-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_secrets_provider_vault/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml
new file mode 100644
index 000000000..20496d0e8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/converge.yml
@@ -0,0 +1,180 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create silence without parameters
+ silence:
+ auth:
+ url: http://localhost:8080
+ ignore_errors: True
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'one of the following is required: subscription, check'"
+
+ - name: Create silence with only check parameter
+ silence:
+ auth:
+ url: http://localhost:8080
+ check: check
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.check == 'check'
+ - result.object.metadata.name == '*:check'
+
+ - name: Create silence with only check parameter idempotence
+ silence:
+ auth:
+ url: http://localhost:8080
+ check: check
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create silence with only subscription parameter
+ silence:
+ auth:
+ url: http://localhost:8080
+ subscription: agent
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.subscription == 'agent'
+ - result.object.metadata.name == 'agent:*'
+
+ - name: Create silence with only subscription parameter idempotence
+ silence:
+ auth:
+ url: http://localhost:8080
+ subscription: agent
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Create a silence
+ silence: &idempotence
+ auth:
+ url: http://localhost:8080
+ subscription: entity:mail-server
+ check: some-check
+ # Schedule silence in the future (2030) in order to prevent it from
+ # expiring and getting dropped on the floor by the backend.
+ begin: 1893452400
+ expire: 120
+ expire_on_resolve: True
+ reason: updating mail server
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.metadata.name == 'entity:mail-server:some-check'
+ - result.object.subscription == 'entity:mail-server'
+ - result.object.check == 'some-check'
+ - result.object.begin == 1893452400
+ - result.object.expire == 120
+ - result.object.expire_on_resolve == True
+ - result.object.creator == 'admin'
+ - result.object.reason == 'updating mail server'
+
+ - name: Test silence creation idempotence
+ silence: *idempotence
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify a silence
+ silence:
+ auth:
+ url: http://localhost:8080
+ subscription: entity:mail-server
+ check: some-check
+ begin: 1893452500
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.begin == 1893452500
+ - result.object.subscription == 'entity:mail-server'
+ - result.object.check == 'some-check'
+ - not result.object.expire_on_resolve
+
+ - name: Create a second silence
+ silence:
+ auth:
+ url: http://localhost:8080
+ subscription: agent
+ check: check-cpu
+
+ - name: Fetch all silences
+ silence_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 4
+ - result.objects.0.check == 'check'
+ - "'subscription' not in result.objects.0"
+
+ - name: Fetch a specific silence
+ silence_info:
+ auth:
+ url: http://localhost:8080
+ subscription: agent
+ check: check-cpu
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.subscription == 'agent'
+ - result.objects.0.check == 'check-cpu'
+
+ - name: Delete a silence
+ silence:
+ auth:
+ url: http://localhost:8080
+ check: check
+ state: absent
+
+ - name: Fetch all silecnes
+ silence_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 3
+ - result.objects.0.subscription == 'agent'
+ - "'check' not in result.objects.0"
+
+ - name: Try to fetch non-existing silence
+ silence_info:
+ auth:
+ url: http://localhost:8080
+ subscription: bad
+ check: bad
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_silence/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml
new file mode 100644
index 000000000..8dcb5fc11
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/converge.yml
@@ -0,0 +1,153 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Create socket handler with missing required parameters
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'state is present but all of the following are missing: type, host, port'"
+
+ - name: Create socket handler with minimal parameters
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: minimal_handler
+ type: tcp
+ host: 10.1.0.99
+ port: 4444
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.socket.host == '10.1.0.99'
+ - result.object.socket.port == 4444
+ - result.object.type == 'tcp'
+ - result.object.metadata.name == 'minimal_handler'
+
+ - name: Create socket handler with minimal parameters idempotence
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: minimal_handler
+ type: tcp
+ host: 10.1.0.99
+ port: 4444
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Create an socket handler with full parameters
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ type: udp
+ mutator: mutate_input
+ timeout: 30
+ filters:
+ - occurances
+ - production
+ host: 10.1.0.99
+ port: 4444
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.socket.host == '10.1.0.99'
+ - result.object.socket.port == 4444
+ - result.object.type == 'udp'
+ - result.object.mutator == 'mutate_input'
+ - result.object.timeout == 30
+ - result.object.filters == ['occurances', 'production']
+
+ - name: Create socket handler with full parameters idempotence
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ type: udp
+ mutator: mutate_input
+ timeout: 30
+ filters:
+ - occurances
+ - production
+ host: 10.1.0.99
+ port: 4444
+ register: result
+
+ - assert:
+ that: result is not changed
+
+ - name: Modify socket handler
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ type: tcp
+ timeout: 60
+ host: 10.1.0.99
+ port: 4444
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.type == 'tcp'
+ - result.object.socket.host == '10.1.0.99'
+ - result.object.socket.port == 4444
+ - not result.object.filters
+ - "'mutator' not in result.object"
+
+ - name: Fetch all socket handlers
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 2
+
+ - name: Fetch a specific socket handler
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'handler'
+
+ - name: Delete socket handler
+ socket_handler:
+ auth:
+ url: http://localhost:8080
+ name: handler
+ state: absent
+
+ - name: Get all socket handlers
+ handler_info:
+ auth:
+ url: http://localhost:8080
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.metadata.name == 'minimal_handler' \ No newline at end of file
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_socket_handler/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml
new file mode 100644
index 000000000..a06da3054
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/converge.yml
@@ -0,0 +1,53 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ tasks:
+ - name: Call tessen with missing required parameters
+ tessen:
+ auth:
+ url: http://localhost:8080
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "result.msg == 'missing required arguments: state'"
+
+ - name: Disable tessen # Tessen is enabled by default on Sensu backends
+ tessen:
+ auth:
+ url: http://localhost:8080
+ state: disabled
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.opt_out == True
+
+ - name: Enable tessen
+ tessen:
+ auth:
+ url: http://localhost:8080
+ state: enabled
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.opt_out == False
+
+ - name: Try to enable already enabled tessen
+ tessen:
+ auth:
+ url: http://localhost:8080
+ state: enabled
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_tessen/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml
new file mode 100644
index 000000000..7312779e6
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/converge.yml
@@ -0,0 +1,198 @@
+---
+- name: Converge
+ collections:
+ - sensu.sensu_go
+ hosts: all
+ gather_facts: no
+ environment:
+ SENSU_URL: http://localhost:8080
+
+ tasks:
+ - name: Create user with minimal parameters
+ user:
+ name: awesome_username
+ password: hidden_password?
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.username == 'awesome_username'
+
+ - name: Minimal parameter idempotence check
+ user:
+ name: awesome_username
+ password: hidden_password?
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Disable enabled user
+ user:
+ name: awesome_username
+ state: disabled
+ register: result
+
+ - assert:
+ that:
+ - result.object.disabled == True
+
+ - name: Add disabled user to some groups
+ user:
+ name: awesome_username
+ groups: [ a, b, c ]
+ state: disabled
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.groups | sort == ['a', 'b', 'c']
+
+ - name: Change password on disabled user
+ user:
+ name: awesome_username
+ password: new_pass
+ state: disabled
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Create a disabled user
+ user:
+ name: test_username
+ password: hidden_password?
+ state: disabled
+ groups:
+ - dev
+ - prod
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.username == 'test_username'
+ - result.object.disabled == True
+ - result.object.groups == ['dev', 'prod']
+
+ - name: Try to disable an already disabled user
+ user:
+ name: test_username
+ state: disabled
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+ - name: Enable a disabled user
+ user:
+ name: test_username
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.disabled == False
+
+ - name: Modify a user
+ user:
+ name: test_username
+ password: hidden_password!
+ groups:
+ - dev
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.object.groups == ['dev']
+
+ - name: Fetch all users
+ user_info:
+ register: result
+
+ - debug:
+ var: result
+
+ - assert:
+ that:
+ - result.objects | length == 4 # users admin and agent already exist
+
+ - name: Fetch specific user
+ user_info:
+ name: test_username
+ register: result
+
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.username == 'test_username'
+ - result.objects.0.disabled == False
+
+ - name: Try to create a user with no password
+ user:
+ name: missing_user
+ state: disabled
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+
+ - name: Try to fetch non-existing user
+ user_info:
+ name: bad-bad-user
+ register: result
+
+ - assert:
+ that:
+ - result.objects == []
+
+ - name: Create a user with password hash (check mode)
+ user: &hash_pass_user
+ name: hash_pass_user
+ password_hash: $5f$14$.brXRviMZpbaleSq9kjoUuwm67V/s4IziOLGHjEqxJbzPsreQAyNm
+ check_mode: true
+ register: result
+ - assert:
+ that:
+ - result is changed
+ - result.object.username == "hash_pass_user"
+
+ - name: Make sure nothing changed
+ user_info:
+ name: hash_pass_user
+ register: result
+ - assert:
+ that:
+ - result.objects | length == 0
+
+ - name: Create a user with password hash
+ user: *hash_pass_user
+ register: result
+ - assert:
+ that:
+ - result is changed
+ - result.object.username == "hash_pass_user"
+
+ - name: Make sure new user appeared
+ user_info:
+ name: hash_pass_user
+ register: result
+ - assert:
+ that:
+ - result.objects | length == 1
+ - result.objects.0.username == "hash_pass_user"
+
+ - name: Create a user with password hash (failed idempotence for hash)
+ user: *hash_pass_user
+ register: result
+ - assert:
+ that:
+ - result is changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/module_user/molecule.yml
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml
new file mode 100644
index 000000000..f00ffa71c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/converge.yml
@@ -0,0 +1,134 @@
+- name: Converge
+ hosts: all
+
+ tasks:
+ - name: Configure overloaded variables
+ include_role:
+ name: sensu.sensu_go.agent
+ tasks_from: configure
+ vars:
+ agent_config:
+ backend-url:
+ - "ws://0.0.0.0:4321"
+
+ - name: Confirm overloaded variables
+ slurp:
+ src: /etc/sensu/agent.yml
+ register: agent_yml
+
+ - assert:
+ that:
+ - |
+ agent_yml.content | b64decode | from_yaml == {
+ "backend-url": ["ws://0.0.0.0:4321"]
+ }
+
+ - name: Configure full set of optional variables
+ include_role:
+ name: sensu.sensu_go.agent
+ tasks_from: configure
+ vars:
+ agent_config:
+ annotations:
+ example-key: "example value"
+ example-key2: "example value 2"
+ backend-url:
+ - "ws://0.0.0.0:4321"
+ cache-dir: /tmp/sensu-agent/cache
+ disable-assets: true
+ allow-list: /etc/sensu/check-allow-list.yaml
+ label:
+ example_label1: "example label"
+ example_label2: "example label 2"
+ name: "agent-01"
+ log-level: fatal
+ api-hosts: "192.168.10.3"
+ api-port: 3031
+ disable-api: true
+ events-burst-limit: 20
+ events-rate-limit: 13.4
+ deregister: false
+ deregister-handler: "deregister"
+ keepalive-interval: 35
+ keepalive-timeout: 215
+ namespace: "ops"
+ user: "sensuadmin"
+ password: "Never2GuessM3"
+ redact:
+ - secret_key
+ - usernames
+ - passwords
+ trusted-ca-file: "/var/lib/sensu/ca.pem"
+ insecure-skip-tls-verify: true
+ socket-host: "0.0.0.0"
+ socket-port: 13030
+ disable-sockets: true
+ statsd-disable: true
+ statsd-event-handlers:
+ - influxdb
+ - opentsdb
+ statsd-flush-interval: 18
+ statsd-metrics-host: 192.168.50.60
+ statsd-metrics-port: 18125
+ exec: "/usr/local/bin/check_disk.sh"
+ sha512: b648feb599b35722248e3d0b0c43ba1c60e4531e9547552e552e7b8dfdb54bb8e1ea414cf33b366ec0a51dd1423bd9a1846b9338771ab41d02a98695d9577834
+ args:
+ - /dev/sda1
+ - /dev/sda2
+ - "-r"
+ enable_env: false
+
+ - name: Confirm full set of optional variables
+ slurp:
+ src: /etc/sensu/agent.yml
+ register: agent_yml
+
+ - debug:
+ var: agent_yml.content | b64decode | from_yaml
+
+ - assert:
+ that:
+ - |
+ agent_yml.content | b64decode | from_yaml == {
+ "allow-list": "/etc/sensu/check-allow-list.yaml",
+ "annotations": {
+ "example-key": "example value",
+ "example-key2": "example value 2",
+ },
+ "api-hosts": "192.168.10.3",
+ "api-port": 3031,
+ "args": ["/dev/sda1", "/dev/sda2", "-r"],
+ "backend-url": ["ws://0.0.0.0:4321"],
+ "cache-dir": "/tmp/sensu-agent/cache",
+ "deregister": False,
+ "deregister-handler": "deregister",
+ "disable-api": True,
+ "disable-assets": True,
+ "disable-sockets": True,
+ "enable_env": False,
+ "events-burst-limit": 20,
+ "events-rate-limit": 13.4,
+ "exec": "/usr/local/bin/check_disk.sh",
+ "insecure-skip-tls-verify": True,
+ "keepalive-interval": 35,
+ "keepalive-timeout": 215,
+ "label": {
+ "example_label1": "example label",
+ "example_label2": "example label 2",
+ },
+ "log-level": "fatal",
+ "name": "agent-01",
+ "namespace": "ops",
+ "password": "Never2GuessM3",
+ "redact": ["secret_key", "usernames", "passwords"],
+ "sha512": "b648feb599b35722248e3d0b0c43ba1c60e4531e9547552e552e7b8dfdb54bb8e1ea414cf33b366ec0a51dd1423bd9a1846b9338771ab41d02a98695d9577834",
+ "socket-host": "0.0.0.0",
+ "socket-port": 13030,
+ "statsd-disable": True,
+ "statsd-event-handlers": ["influxdb", "opentsdb"],
+ "statsd-flush-interval": 18,
+ "statsd-metrics-host": "192.168.50.60",
+ "statsd-metrics-port": 18125,
+ "trusted-ca-file": "/var/lib/sensu/ca.pem",
+ "user": "sensuadmin",
+ }
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml
new file mode 100644
index 000000000..7ba0b239f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/molecule.yml
@@ -0,0 +1,14 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - prepare
+ - converge
+ - destroy
+
+platforms:
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml
new file mode 100644
index 000000000..40ba62238
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_config/prepare.yml
@@ -0,0 +1,24 @@
+---
+- name: Prepare
+ hosts: all
+
+ tasks:
+ - name: Create sensu group
+ group:
+ name: sensu
+
+ - name: Create sensu user
+ # We need FQCN here because we are running test from within the
+ # collection. In this case, our collection becomes the default
+ # collection and so the sensu.sensu_go.user module shadows the builtin
+ # one.
+ ansible.builtin.user:
+ name: sensu
+ groups: sensu
+
+ - name: Create /etc/sensu folder
+ file:
+ state: directory
+ path: /etc/sensu
+ owner: sensu
+ group: sensu
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml
new file mode 100644
index 000000000..f56054f04
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/converge.yml
@@ -0,0 +1,5 @@
+---
+- name: Converge
+ hosts: agents
+ roles:
+ - sensu.sensu_go.agent
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml
new file mode 100644
index 000000000..505b22632
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/molecule.yml
@@ -0,0 +1,30 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - converge
+ - verify
+ - check
+ - destroy
+
+platforms:
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
+ groups: [ agents ]
+ override_command: false
+ privileged: true
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
+
+# Upstream container - just to populate the inventory
+provisioner:
+ inventory:
+ hosts:
+ all:
+ children:
+ backends:
+ hosts:
+ upstream-backend:
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml
new file mode 100644
index 000000000..bbe821561
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_default/verify.yml
@@ -0,0 +1,28 @@
+---
+- name: Verify
+ hosts: agents
+
+ tasks:
+ - name: Agent configuration must exist
+ stat:
+ path: /etc/sensu/agent.yml
+ register: result
+
+ - assert:
+ that:
+ - result.stat.exists
+ - result.stat.mode == '0600'
+ - result.stat.pw_name == 'sensu'
+ - result.stat.gr_name == 'sensu'
+
+ - name: Confirm default configuration settings
+ slurp:
+ src: /etc/sensu/agent.yml
+ register: agent_yml
+
+ - assert:
+ that:
+ - |
+ agent_yml.content | b64decode | from_yaml == {
+ "backend-url": ["ws://upstream-backend:8081"],
+ }
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml
new file mode 100644
index 000000000..7e0e1ccfa
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/converge.yml
@@ -0,0 +1,71 @@
+---
+- name: Pre-converge secure backend config step
+ hosts: backends
+ gather_facts: no
+ tasks:
+ - name: Set dummy backend PKI variables
+ set_fact:
+ api_key_file: /tmp/dummy.key
+
+ - debug:
+ msg: "{{ hostvars['upstream-backend']['api_key_file'] }}"
+
+- name: Converge
+ hosts: agents
+ vars:
+ agent_trusted_ca_file: files/sensu-agent-trusted-ca.crt
+ roles:
+ - sensu.sensu_go.agent
+
+- name: Verify configure_agent
+ hosts: agents
+ tasks:
+ - name: The trusted CA store file must exist
+ stat:
+ path: /etc/sensu/sensu-agent-trusted-ca.crt
+ register: result
+
+ - assert:
+ that:
+ - "{{ result.stat.exists }}"
+ - "{{ result.stat.pw_name == 'sensu' }}"
+ - "{{ result.stat.gr_name == 'sensu' }}"
+ - "{{ result.stat.mode == '0644' }}"
+
+ - name: Confirm secured agent configuration settings
+ lineinfile:
+ path: &agent_yml /etc/sensu/agent.yml
+ line: '{{ item }}'
+ with_items:
+ - 'backend-url:'
+ - '- wss://upstream-backend:8081'
+ - 'trusted-ca-file: /etc/sensu/sensu-agent-trusted-ca.crt'
+ - 'insecure-skip-tls-verify: false'
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+
+- name: Default configuration
+ hosts: agents
+ gather_facts: no
+ roles:
+ - sensu.sensu_go.agent
+
+- name: Verify default configuration
+ hosts: agents
+ tasks:
+ - name: Confirm that none of secured configuration settings leak in
+ lineinfile:
+ path: *agent_yml
+ regexp: '{{ item }}'
+ state: absent
+ with_items:
+ - '^trusted-ca-file:'
+ - '^insecure-skip-tls-verify:'
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt
new file mode 100644
index 000000000..fe9c08f48
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y
+OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3
+8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS
+KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u
+53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb
+7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg
+vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw
+gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv
+NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB
+ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB
+BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in
+NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m
+tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K
+3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ
+RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ
+6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml
new file mode 100644
index 000000000..430054e8b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_agent_secured/molecule.yml
@@ -0,0 +1,21 @@
+---
+platforms:
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
+ groups: [ agents ]
+ override_command: false
+ privileged: true
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
+
+# Upstream container - just to populate the inventory
+provisioner:
+ inventory:
+ hosts:
+ all:
+ children:
+ backends:
+ hosts:
+ upstream-backend:
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml
new file mode 100644
index 000000000..056998447
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/converge.yml
@@ -0,0 +1,74 @@
+---
+- name: Converge
+ hosts: all
+
+ tasks:
+ - name: Configure select set of optional variables
+ include_role:
+ name: sensu.sensu_go.backend
+ tasks_from: configure
+ vars:
+ backend_config:
+ debug: no
+ log-level: debug
+ api-listen-address: "[::]:4430"
+
+ - name: Confirm optional configuration settings
+ slurp:
+ src: /etc/sensu/backend.yml
+ register: backend_yml
+
+ - assert:
+ that:
+ - |
+ backend_yml.content | b64decode | from_yaml == {
+ "state-dir": "/var/lib/sensu/sensu-backend",
+ "debug": False,
+ "log-level": "debug",
+ "api-listen-address": "[::]:4430",
+ }
+
+ - name: Configure full set of optional variables
+ include_role:
+ name: sensu.sensu_go.backend
+ tasks_from: configure
+ vars:
+ backend_config:
+ debug: yes
+ log-level: debug
+ state-dir: /tmp/different/state
+ deregistration-handler: /tmp/handler.sh
+ agent-host: "127.0.0.1"
+ agent-port: 4431
+ api-listen-address: "[::]:4430"
+ api-url: "http://localhost:4432"
+ dashboard-host: "192.168.10.6"
+ dashboard-port: 4433
+ etcd-initial-advertise-peer-urls:
+ - https://10.10.0.1:2380
+ - https://10.20.0.1:2380
+
+ - name: Confirm full configuration settings
+ slurp:
+ src: /etc/sensu/backend.yml
+ register: backend_yml
+
+ - assert:
+ that:
+ - |
+ backend_yml.content | b64decode | from_yaml == {
+ "debug": True,
+ "log-level": "debug",
+ "state-dir": "/tmp/different/state",
+ "deregistration-handler": "/tmp/handler.sh",
+ "agent-host": "127.0.0.1",
+ "agent-port": 4431,
+ "api-listen-address": "[::]:4430",
+ "api-url": "http://localhost:4432",
+ "dashboard-host": "192.168.10.6",
+ "dashboard-port": 4433,
+ "etcd-initial-advertise-peer-urls": [
+ "https://10.10.0.1:2380",
+ "https://10.20.0.1:2380",
+ ]
+ }
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml
new file mode 100644
index 000000000..7ba0b239f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/molecule.yml
@@ -0,0 +1,14 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - prepare
+ - converge
+ - destroy
+
+platforms:
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml
new file mode 100644
index 000000000..40ba62238
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_config/prepare.yml
@@ -0,0 +1,24 @@
+---
+- name: Prepare
+ hosts: all
+
+ tasks:
+ - name: Create sensu group
+ group:
+ name: sensu
+
+ - name: Create sensu user
+ # We need FQCN here because we are running test from within the
+ # collection. In this case, our collection becomes the default
+ # collection and so the sensu.sensu_go.user module shadows the builtin
+ # one.
+ ansible.builtin.user:
+ name: sensu
+ groups: sensu
+
+ - name: Create /etc/sensu folder
+ file:
+ state: directory
+ path: /etc/sensu
+ owner: sensu
+ group: sensu
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml
new file mode 100644
index 000000000..69becbbfa
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/converge.yml
@@ -0,0 +1,8 @@
+---
+- name: Converge
+ hosts: all
+
+ tasks:
+ - name: Install backend
+ include_role:
+ name: sensu.sensu_go.backend
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml
new file mode 100644
index 000000000..f9c28ce1a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/molecule.yml
@@ -0,0 +1,20 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - converge
+ - idempotence
+ - verify
+ - check
+ - destroy
+
+platforms:
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
+ override_command: false
+ privileged: true
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml
new file mode 100644
index 000000000..c6d487eaa
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_default/verify.yml
@@ -0,0 +1,35 @@
+---
+- name: Verify
+ hosts: all
+
+ tasks:
+ - name: Backend configuration must exist
+ stat:
+ path: /etc/sensu/backend.yml
+ register: result
+
+ - assert:
+ that:
+ - result.stat.exists
+ - result.stat.mode == '0600'
+ - result.stat.pw_name == 'sensu'
+ - result.stat.gr_name == 'sensu'
+
+ - name: Confirm default configuration settings
+ slurp:
+ src: /etc/sensu/backend.yml
+ register: backend_yml
+
+ - assert:
+ that:
+ - |
+ backend_yml.content | b64decode | from_yaml == {
+ "state-dir": "/var/lib/sensu/sensu-backend",
+ }
+
+ - name: Make sure login works
+ uri:
+ url: http://localhost:8080/auth
+ url_username: admin
+ url_password: P@ssw0rd!
+ force_basic_auth: yes
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml
new file mode 100644
index 000000000..fd6861879
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/converge.yml
@@ -0,0 +1,164 @@
+---
+- name: Converge
+ hosts: all
+
+ tasks:
+ - name: Install secured backend
+ include_role:
+ name: sensu.sensu_go.backend
+ tasks_from: configure
+ vars:
+ etcd_cert_file: files/etcd-client.crt
+ etcd_key_file: files/etcd-client.key
+ etcd_trusted_ca_file: files/client-ca.crt
+ etcd_peer_cert_file: files/etcd-peer.crt
+ etcd_peer_key_file: files/etcd-peer.key
+ etcd_peer_trusted_ca_file: files/etcd-peer-ca.crt
+ api_cert_file: files/sensu-api.crt
+ api_key_file: files/sensu-api.key
+ api_trusted_ca_file: files/sensu-api-ca.crt
+ dashboard_cert_file: files/sensu-dashboard.crt
+ dashboard_key_file: files/sensu-dashboard.key
+
+ - name: The public keys must exist
+ stat:
+ path: "{{ item }}"
+ register: result
+ loop:
+ - /etc/sensu/etcd-client.crt
+ - /etc/sensu/etcd-client-ca.crt
+ - /etc/sensu/etcd-peer.crt
+ - /etc/sensu/etcd-peer-ca.crt
+ - /etc/sensu/api.crt
+ - /etc/sensu/api-ca.crt
+ - /etc/sensu/dashboard.crt
+
+ - assert:
+ that:
+ - item.stat.exists
+ - item.stat.pw_name == 'sensu'
+ - item.stat.gr_name == 'sensu'
+ - item.stat.mode == '0644'
+ loop: "{{ result.results }}"
+
+ - name: The private keys must exist and be protected
+ stat:
+ path: "{{ item }}"
+ register: result
+ loop:
+ - /etc/sensu/etcd-client.key
+ - /etc/sensu/etcd-peer.key
+ - /etc/sensu/api.key
+ - /etc/sensu/dashboard.key
+
+ - assert:
+ that:
+ - item.stat.exists
+ - item.stat.pw_name == 'sensu'
+ - item.stat.gr_name == 'sensu'
+ - item.stat.mode == '0400'
+ loop: "{{ result.results }}"
+
+ - name: Confirm secured configuration settings
+ slurp:
+ src: /etc/sensu/backend.yml
+ register: backend_yml
+
+ - assert:
+ that:
+ - |
+ backend_yml.content | b64decode | from_yaml == {
+ "state-dir": "/var/lib/sensu/sensu-backend",
+ "etcd-listen-client-urls": "https://localhost:2379",
+ "etcd-listen-peer-urls": "https://localhost:2380",
+ "etcd-initial-advertise-peer-urls": "https://localhost:2380",
+ "etcd-initial-cluster": "default=https://localhost:2380",
+ "etcd-cert-file": "/etc/sensu/etcd-client.crt",
+ "etcd-key-file": "/etc/sensu/etcd-client.key",
+ "etcd-trusted-ca-file": "/etc/sensu/etcd-client-ca.crt",
+ "etcd-client-cert-auth": True,
+ "etcd-peer-cert-file": "/etc/sensu/etcd-peer.crt",
+ "etcd-peer-key-file": "/etc/sensu/etcd-peer.key",
+ "etcd-peer-client-cert-auth": True,
+ "etcd-peer-trusted-ca-file": "/etc/sensu/etcd-peer-ca.crt",
+ "cert-file": "/etc/sensu/api.crt",
+ "key-file": "/etc/sensu/api.key",
+ "trusted-ca-file": "/etc/sensu/api-ca.crt",
+ "insecure-skip-tls-verify": False,
+ "api-url": "https://localhost:8080",
+ "dashboard-cert-file": "/etc/sensu/dashboard.crt",
+ "dashboard-key-file": "/etc/sensu/dashboard.key",
+ }
+
+ - name: Configure an overriding of managed vars
+ include_role:
+ name: sensu.sensu_go.backend
+ tasks_from: configure
+ vars:
+ etcd_cert_file: files/etcd-client.crt
+ etcd_key_file: files/etcd-client.key
+ etcd_trusted_ca_file: files/client-ca.crt
+ etcd_peer_cert_file: files/etcd-peer.crt
+ etcd_peer_key_file: files/etcd-peer.key
+ etcd_peer_trusted_ca_file: files/etcd-peer-ca.crt
+ api_cert_file: files/sensu-api.crt
+ api_key_file: files/sensu-api.key
+ api_trusted_ca_file: files/sensu-api-ca.crt
+ dashboard_cert_file: files/sensu-dashboard.crt
+ dashboard_key_file: files/sensu-dashboard.key
+ backend_config:
+ debug: true
+ log-level: debug
+ state-dir: /tmp/different-state
+ etcd-listen-client-urls: "https://127.0.0.1:2379"
+ etcd-listen-peer-urls: "https://127.0.0.1:2380"
+ etcd-initial-advertise-peer-urls: "https://127.0.0.1:2380"
+ etcd-initial-cluster: "default=https://127.0.0.1:2380"
+ etcd-cert-file: "/etc/sensu/../sensu/etcd-client.crt"
+ etcd-key-file: "/etc/sensu/../sensu/etcd-client.key"
+ etcd-trusted-ca-file: "/etc/sensu/../sensu/etcd-client-ca.crt"
+ etcd-client-cert-auth: false
+ etcd-peer-cert-file: "/etc/sensu/../sensu/etcd-peer.crt"
+ etcd-peer-key-file: "/etc/sensu/../sensu/etcd-peer.key"
+ etcd-peer-client-cert-auth: false
+ etcd-peer-trusted-ca-file: "/etc/sensu/../sensu/etcd-peer-ca.crt"
+ cert-file: "/etc/sensu/../sensu/api.crt"
+ key-file: "/etc/sensu/../sensu/api.key"
+ trusted-ca-file: "/etc/sensu/../sensu/api-ca.crt"
+ insecure-skip-tls-verify: true
+ api-url: "https://127.0.0.1:8080"
+ dashboard-cert-file: /etc/sensu/../sensu/dashboard.crt
+ dashboard-key-file: /etc/sensu/../sensu/dashboard.key
+
+ - name: Confirm overriding of managed vars configuration settings
+ slurp:
+ src: /etc/sensu/backend.yml
+ register: backend_yml
+
+ - assert:
+ that:
+ - |
+ backend_yml.content | b64decode | from_yaml == {
+ "api-url": "https://127.0.0.1:8080",
+ "cert-file": "/etc/sensu/../sensu/api.crt",
+ "dashboard-cert-file": "/etc/sensu/../sensu/dashboard.crt",
+ "dashboard-key-file": "/etc/sensu/../sensu/dashboard.key",
+ "debug": True,
+ "etcd-cert-file": "/etc/sensu/../sensu/etcd-client.crt",
+ "etcd-client-cert-auth": False,
+ "etcd-initial-advertise-peer-urls": "https://127.0.0.1:2380",
+ "etcd-initial-cluster": "default=https://127.0.0.1:2380",
+ "etcd-key-file": "/etc/sensu/../sensu/etcd-client.key",
+ "etcd-listen-client-urls": "https://127.0.0.1:2379",
+ "etcd-listen-peer-urls": "https://127.0.0.1:2380",
+ "etcd-peer-cert-file": "/etc/sensu/../sensu/etcd-peer.crt",
+ "etcd-peer-client-cert-auth": False,
+ "etcd-peer-key-file": "/etc/sensu/../sensu/etcd-peer.key",
+ "etcd-peer-trusted-ca-file": "/etc/sensu/../sensu/etcd-peer-ca.crt",
+ "etcd-trusted-ca-file": "/etc/sensu/../sensu/etcd-client-ca.crt",
+ "insecure-skip-tls-verify": True,
+ "key-file": "/etc/sensu/../sensu/api.key",
+ "log-level": "debug",
+ "state-dir": "/tmp/different-state",
+ "trusted-ca-file": "/etc/sensu/../sensu/api-ca.crt",
+ }
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt
new file mode 100644
index 000000000..fe9c08f48
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/client-ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y
+OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3
+8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS
+KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u
+53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb
+7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg
+vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw
+gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv
+NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB
+ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB
+BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in
+NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m
+tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K
+3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ
+RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ
+6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt
new file mode 100644
index 000000000..85b705566
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.crt
@@ -0,0 +1,87 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 70:e5:70:1d:0c:bb:93:ba:81:72:b6:5b:47:4f:9a:28
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: CN=Sensu-test CA
+ Validity
+ Not Before: Nov 6 17:22:40 2019 GMT
+ Not After : Oct 21 17:22:40 2022 GMT
+ Subject: CN=etcd-client
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:d3:ac:be:a4:45:22:ed:8f:0b:b2:19:0d:ab:60:
+ db:0b:c4:fb:8a:73:22:9a:f8:54:16:a0:4c:12:00:
+ a1:21:ef:03:ca:83:29:8e:90:12:87:03:78:12:0e:
+ 4f:b9:1c:f0:a2:57:06:51:fe:9c:6a:ef:45:9a:6b:
+ d5:32:df:58:44:ea:9b:ed:4d:e1:19:df:ce:ed:ea:
+ 90:a4:b1:2e:55:a2:6c:87:53:13:38:de:db:cc:f6:
+ cc:49:ba:85:aa:29:3d:0e:c9:6a:c0:b8:85:16:a7:
+ 34:1a:2a:d9:cc:f4:0e:1e:68:35:6d:4a:dd:5d:43:
+ f6:70:da:0a:27:5e:77:ee:8c:d9:46:f8:70:2e:e3:
+ 3c:97:7d:55:c7:c6:0d:e7:20:61:df:9e:59:78:97:
+ 6c:0d:5c:ed:d3:c8:d7:d8:f3:7c:7b:58:2f:0a:f9:
+ b3:a5:63:41:b6:6a:e6:b2:bc:48:eb:cb:4b:76:2b:
+ 2e:3e:9c:1a:db:c9:f1:2f:1a:7c:43:db:38:6b:9b:
+ 7f:d0:d7:25:7c:3f:50:1d:70:11:06:a8:d6:14:b5:
+ c4:dd:ea:3d:28:d9:57:b7:aa:d2:26:01:cc:5e:f1:
+ ae:48:28:a1:4a:ee:ac:ed:9c:7e:61:ef:37:12:67:
+ 7b:d9:05:0b:84:51:75:be:04:96:89:b7:d1:95:d6:
+ ca:61
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ C5:76:B1:3A:CC:A2:6C:9B:DD:43:D2:59:E1:E2:51:65:A0:59:09:70
+ X509v3 Authority Key Identifier:
+ keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9
+ DirName:/CN=Sensu-test CA
+ serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature
+ X509v3 Subject Alternative Name:
+ IP Address:176.16.117.105
+ Signature Algorithm: sha256WithRSAEncryption
+ 22:8f:57:1e:79:d1:78:9f:d4:fa:c5:41:9d:b1:b0:90:99:d4:
+ 85:4d:83:0e:ec:c6:0d:fa:a5:4b:7f:16:70:58:9f:5e:1a:f4:
+ 15:cf:45:2f:cc:69:8e:56:bd:f9:d1:96:6a:34:b5:96:12:b4:
+ 84:07:21:8a:6f:13:ac:a2:6e:e0:e3:63:17:f3:33:86:ed:c5:
+ ea:48:c1:b3:af:05:75:28:5f:4f:77:5f:6c:83:cc:50:09:e4:
+ 56:59:e3:ec:b0:f1:56:01:ca:d0:69:90:b3:e3:a4:a5:c0:cd:
+ 99:5e:65:ad:33:30:42:53:70:d7:47:c8:4c:47:fd:b6:b1:b1:
+ f6:cb:0d:8b:6c:ab:14:f5:75:1e:3b:20:f9:00:05:87:bf:e0:
+ a7:57:12:af:e9:67:9d:5a:96:c3:52:55:25:a8:ec:86:20:49:
+ 74:e8:6a:19:c0:4a:27:5b:fc:8a:4d:a4:5e:6d:fa:9a:91:48:
+ c5:e4:84:e1:ed:f9:d5:6e:21:69:6e:20:4a:0e:cb:44:d7:4a:
+ 38:10:23:12:9f:9b:48:d0:a3:9f:02:e4:84:6d:d9:be:17:eb:
+ 60:d0:d3:39:60:60:13:7a:b1:a2:38:e4:75:5b:28:4e:5d:25:
+ 35:04:52:a2:c1:ce:8b:94:9f:37:30:3c:a8:35:36:44:db:ed:
+ b2:6f:4a:b1
+-----BEGIN CERTIFICATE-----
+MIIDbjCCAlagAwIBAgIQcOVwHQy7k7qBcrZbR0+aKDANBgkqhkiG9w0BAQsFADAY
+MRYwFAYDVQQDDA1TZW5zdS10ZXN0IENBMB4XDTE5MTEwNjE3MjI0MFoXDTIyMTAy
+MTE3MjI0MFowFjEUMBIGA1UEAwwLZXRjZC1jbGllbnQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDTrL6kRSLtjwuyGQ2rYNsLxPuKcyKa+FQWoEwSAKEh
+7wPKgymOkBKHA3gSDk+5HPCiVwZR/pxq70Waa9Uy31hE6pvtTeEZ387t6pCksS5V
+omyHUxM43tvM9sxJuoWqKT0OyWrAuIUWpzQaKtnM9A4eaDVtSt1dQ/Zw2gonXnfu
+jNlG+HAu4zyXfVXHxg3nIGHfnll4l2wNXO3TyNfY83x7WC8K+bOlY0G2auayvEjr
+y0t2Ky4+nBrbyfEvGnxD2zhrm3/Q1yV8P1AdcBEGqNYUtcTd6j0o2Ve3qtImAcxe
+8a5IKKFK7qztnH5h7zcSZ3vZBQuEUXW+BJaJt9GV1sphAgMBAAGjgbUwgbIwCQYD
+VR0TBAIwADAdBgNVHQ4EFgQUxXaxOsyibJvdQ9JZ4eJRZaBZCXAwUwYDVR0jBEww
+SoAUZW81fRELC/07z+Y7lfo1a4ANJ+mhHKQaMBgxFjAUBgNVBAMMDVNlbnN1LXRl
+c3QgQ0GCFC3obG+lDn1jqLELPYYdLtBiXyFHMBMGA1UdJQQMMAoGCCsGAQUFBwMC
+MAsGA1UdDwQEAwIHgDAPBgNVHREECDAGhwSwEHVpMA0GCSqGSIb3DQEBCwUAA4IB
+AQAij1ceedF4n9T6xUGdsbCQmdSFTYMO7MYN+qVLfxZwWJ9eGvQVz0UvzGmOVr35
+0ZZqNLWWErSEByGKbxOsom7g42MX8zOG7cXqSMGzrwV1KF9Pd19sg8xQCeRWWePs
+sPFWAcrQaZCz46SlwM2ZXmWtMzBCU3DXR8hMR/22sbH2yw2LbKsU9XUeOyD5AAWH
+v+CnVxKv6WedWpbDUlUlqOyGIEl06GoZwEonW/yKTaRebfqakUjF5ITh7fnVbiFp
+biBKDstE10o4ECMSn5tI0KOfAuSEbdm+F+tg0NM5YGATerGiOOR1WyhOXSU1BFKi
+wc6LlJ83MDyoNTZE2+2yb0qx
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key
new file mode 100644
index 000000000..1bed38d5c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTrL6kRSLtjwuy
+GQ2rYNsLxPuKcyKa+FQWoEwSAKEh7wPKgymOkBKHA3gSDk+5HPCiVwZR/pxq70Wa
+a9Uy31hE6pvtTeEZ387t6pCksS5VomyHUxM43tvM9sxJuoWqKT0OyWrAuIUWpzQa
+KtnM9A4eaDVtSt1dQ/Zw2gonXnfujNlG+HAu4zyXfVXHxg3nIGHfnll4l2wNXO3T
+yNfY83x7WC8K+bOlY0G2auayvEjry0t2Ky4+nBrbyfEvGnxD2zhrm3/Q1yV8P1Ad
+cBEGqNYUtcTd6j0o2Ve3qtImAcxe8a5IKKFK7qztnH5h7zcSZ3vZBQuEUXW+BJaJ
+t9GV1sphAgMBAAECggEAX+J1bUYgH0pX+vIcXhB+ySMO6tVizJ5GwQUV0GXWl9+E
+cRzfG0QqEFzC7DLtbCYu3ura4xOZ2VrPxbapGeVQP8+imGlZ2XWnb+B8aw4Tthjg
+XqCEKZPSL1NwkMlcOQt7LBKTN/+d0fglwuC8TnoTVzTPVtW91rytistKJDFH1JfI
+AwOyMoWkNYP3zDdMJn9dWkOXsNjK2VoxzCh/DhOcI/2gO4v7fM1w8A8iGYi5uMD4
+b1IzIqcvdgeyGEXhzu2PcJ0Kr1dR2B2VGJJ4kUqIjUGxG7gSE8X0siEBmSBNBou9
+lFp35esx2ffEnzPZaxAdqOpW0eV/iWxxYKFKX37PAQKBgQDpBOxqgbaGqu20MBVB
+rQWh4CjalnvDbjyCyLfAb/6iI8MLnSvw9nfUS9X0TGnMmbZ5gk+7LDKczfs6NOHC
+qBs34tAHHebimYqNII5UFc6OyYHpZsLmCG7+JgJDDD299jyZsNHBuw46TUOwdj9T
+rRUdAX4s5fDzLmNzu/vN7PqXcQKBgQDojO8qscWy8GWbbxUpRjCetxNPpBf83rke
+SL8o283ziMNmhn2L1RHa5vAYBHU1n2aIbYJiU6UEoJnCCpjxcigZdEacE7wsx8Et
+alCzlitLuCW0C3bdvxERm1Ya9frhxq+ljE9Kjn8IQ9+1+4zE0Xzk9bnEQVQsySWk
+siJUay9J8QKBgQCu6iY5cPM9tZNHfgyGx0WCFM02AF4Y/mfn2Jmul6MxvNyNnEa+
+05RxxRdvEekdT4ldPsdw/iVj9W8Pa2DIiP4dfmGf5f9Ju+34MCcG0XPDVVnyhVPp
+7wy6NHfgMpEqRmuJBuT9otd0Rkl4bdrtifBeXJ+FPnoXYYv/9W14T9pv0QKBgBpn
+Tgxp7Ml5VAAG578s8f5DSRUEy5hxbVFL7zBjbInXIGB2qrCCu6lACUig0PIKrCiX
+TnN9jcHtvidy7pSTYowpUI8OCpKHB57xcEJDrZzGRrmfh5p7xCNcoLUk8pxJ482H
+FcWgUjoNAsx1yDDcnDKe1725sSX4nKaLdyxgkxjxAoGAVzuCl7Kl2RjOCIWKG46N
+vIl0nCdyTlJb1ammNr2cxIkjreSjenYS+Y8aXJOVVVDux+QB2LiQioMBDN/KIQ0k
+55enQr0iKrjNIa5GeGN8uNnsrUoJ/9YfRsEq3rokns7kvSdsNFGFH4UOwuuEd4Qb
+y6OFkERhzgGm4TcsRPeOPiM=
+-----END PRIVATE KEY-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt
new file mode 100644
index 000000000..fe9c08f48
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y
+OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3
+8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS
+KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u
+53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb
+7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg
+vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw
+gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv
+NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB
+ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB
+BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in
+NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m
+tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K
+3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ
+RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ
+6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt
new file mode 100644
index 000000000..90e866405
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.crt
@@ -0,0 +1,87 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ ae:53:a2:43:b9:f9:fc:21:b9:f1:2c:e3:c3:b4:45:14
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: CN=Sensu-test CA
+ Validity
+ Not Before: Nov 6 17:22:40 2019 GMT
+ Not After : Oct 21 17:22:40 2022 GMT
+ Subject: CN=etcd-peer
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:dd:2a:35:47:b2:6d:77:0d:d5:a2:16:50:20:97:
+ 93:e9:f6:c4:01:04:b7:2a:62:04:52:01:68:5c:98:
+ f2:e5:82:f6:eb:9a:77:9b:ca:0a:9f:61:34:4d:f4:
+ b5:ae:e0:bd:8a:5d:79:03:b4:10:c3:ea:f3:8c:e2:
+ 88:79:ff:68:2a:dd:d5:d1:d5:68:9a:61:2d:0b:49:
+ 03:ae:fc:1b:26:a7:89:1f:fb:76:e2:81:81:72:bd:
+ 42:c2:9e:60:d9:0d:2e:72:81:c3:11:c6:cd:63:64:
+ 52:a9:5d:d5:85:c1:35:b0:93:2a:de:d3:46:2c:e4:
+ 10:d5:7d:92:e9:3c:ff:f5:b4:7e:e2:fc:fd:6b:9e:
+ c1:a1:a0:c9:6c:31:ad:d2:42:09:55:9e:de:ee:37:
+ 06:40:c7:61:da:c9:5e:d8:40:cb:3f:1f:7b:93:11:
+ c3:e5:5c:9d:e5:fc:77:67:92:de:c3:8a:3e:87:15:
+ dc:47:ad:34:17:84:b4:b2:21:8e:8f:2f:d8:3a:92:
+ 99:4c:80:d7:06:77:5d:3c:66:db:53:15:ee:a5:5f:
+ 12:29:19:07:30:a1:71:7b:d5:1f:ee:24:b3:9c:02:
+ 6e:b6:42:cd:19:96:73:e3:c7:c4:20:c5:a4:75:b4:
+ b5:85:43:68:e4:91:5f:3d:fa:ed:0e:34:f1:fe:a9:
+ 0c:a5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 75:26:89:17:1C:ED:2A:53:BD:CE:87:EA:34:F5:C6:76:7C:C3:B5:08
+ X509v3 Authority Key Identifier:
+ keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9
+ DirName:/CN=Sensu-test CA
+ serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ X509v3 Subject Alternative Name:
+ IP Address:176.16.117.105
+ Signature Algorithm: sha256WithRSAEncryption
+ 3a:39:39:8c:d5:9d:ca:88:0e:d3:c4:b2:e5:91:3b:b4:66:5e:
+ a3:92:44:f4:b4:fa:20:b0:a7:ee:6d:69:f4:8e:51:db:7b:e8:
+ 50:b9:15:00:2a:20:17:4e:0d:bf:4a:62:56:3e:cf:be:33:f6:
+ 44:54:c9:5e:22:1a:7a:3f:40:74:77:ed:96:e9:09:9e:27:dd:
+ 0a:69:bb:d3:5b:b2:4e:95:08:00:71:c1:a6:a2:ed:b9:46:41:
+ 94:96:6a:10:57:d4:7a:c2:0d:ce:c3:14:0c:ff:ab:fe:6e:09:
+ fd:18:09:32:d9:7c:f8:a4:dd:0c:fd:56:55:36:ae:03:fc:59:
+ a3:c5:4b:6f:2e:be:5f:d7:cd:bd:7d:de:3d:29:1a:5b:78:85:
+ 7a:4c:59:f3:15:31:e6:93:9c:db:28:c8:10:86:6f:30:db:3f:
+ 59:29:b1:05:9f:9b:b5:2e:20:b2:b8:dc:2e:3b:c2:75:dc:bf:
+ 39:eb:db:d6:5c:f7:cc:b5:61:ea:a8:c0:45:89:5b:c2:1a:fa:
+ ce:a7:69:63:4b:f6:3c:be:4c:f2:cf:2b:bf:5b:d4:f1:b8:e3:
+ 2a:e2:50:3a:62:e5:0d:42:fc:ac:63:b2:76:3d:c4:0a:26:70:
+ 60:16:ec:12:d7:60:04:10:bd:e7:9d:7c:3c:65:ab:25:51:f7:
+ aa:6b:5e:b1
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIRAK5TokO5+fwhufEs48O0RRQwDQYJKoZIhvcNAQELBQAw
+GDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyNDBaFw0yMjEw
+MjExNzIyNDBaMBQxEjAQBgNVBAMMCWV0Y2QtcGVlcjCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAN0qNUeybXcN1aIWUCCXk+n2xAEEtypiBFIBaFyY8uWC
+9uuad5vKCp9hNE30ta7gvYpdeQO0EMPq84ziiHn/aCrd1dHVaJphLQtJA678Gyan
+iR/7duKBgXK9QsKeYNkNLnKBwxHGzWNkUqld1YXBNbCTKt7TRizkENV9kuk8//W0
+fuL8/WuewaGgyWwxrdJCCVWe3u43BkDHYdrJXthAyz8fe5MRw+VcneX8d2eS3sOK
+PocV3EetNBeEtLIhjo8v2DqSmUyA1wZ3XTxm21MV7qVfEikZBzChcXvVH+4ks5wC
+brZCzRmWc+PHxCDFpHW0tYVDaOSRXz367Q408f6pDKUCAwEAAaOBtTCBsjAJBgNV
+HRMEAjAAMB0GA1UdDgQWBBR1JokXHO0qU73Oh+o09cZ2fMO1CDBTBgNVHSMETDBK
+gBRlbzV9EQsL/TvP5juV+jVrgA0n6aEcpBowGDEWMBQGA1UEAwwNU2Vuc3UtdGVz
+dCBDQYIULehsb6UOfWOosQs9hh0u0GJfIUcwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
+CwYDVR0PBAQDAgWgMA8GA1UdEQQIMAaHBLAQdWkwDQYJKoZIhvcNAQELBQADggEB
+ADo5OYzVncqIDtPEsuWRO7RmXqOSRPS0+iCwp+5tafSOUdt76FC5FQAqIBdODb9K
+YlY+z74z9kRUyV4iGno/QHR37ZbpCZ4n3Qppu9Nbsk6VCABxwaai7blGQZSWahBX
+1HrCDc7DFAz/q/5uCf0YCTLZfPik3Qz9VlU2rgP8WaPFS28uvl/Xzb193j0pGlt4
+hXpMWfMVMeaTnNsoyBCGbzDbP1kpsQWfm7UuILK43C47wnXcvznr29Zc98y1Yeqo
+wEWJW8Ia+s6naWNL9jy+TPLPK79b1PG44yriUDpi5Q1C/KxjsnY9xAomcGAW7BLX
+YAQQveedfDxlqyVR96prXrE=
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key
new file mode 100644
index 000000000..3d3dfd47b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/etcd-peer.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDdKjVHsm13DdWi
+FlAgl5Pp9sQBBLcqYgRSAWhcmPLlgvbrmnebygqfYTRN9LWu4L2KXXkDtBDD6vOM
+4oh5/2gq3dXR1WiaYS0LSQOu/Bsmp4kf+3bigYFyvULCnmDZDS5ygcMRxs1jZFKp
+XdWFwTWwkyre00Ys5BDVfZLpPP/1tH7i/P1rnsGhoMlsMa3SQglVnt7uNwZAx2Ha
+yV7YQMs/H3uTEcPlXJ3l/Hdnkt7Dij6HFdxHrTQXhLSyIY6PL9g6kplMgNcGd108
+ZttTFe6lXxIpGQcwoXF71R/uJLOcAm62Qs0ZlnPjx8QgxaR1tLWFQ2jkkV89+u0O
+NPH+qQylAgMBAAECggEALk6aHUr0tIrHAksdt9VE+SXb4EK2fz9KnEkDKvAzW27S
+eH49MPdaxgg6RWBJcjZIOWJc5jOblwnouMTtwm2ByAfuryK55ikWn1hIVykeHjfR
+9EpYmBB5pCaQheNXb9rcsMkOqPgxJPBqhl/JR1ou/auyvDkMZnXSOIE0c3V8OluO
+nL39RHNCz76bNZSpe6BbYH6Ch8xkhCuYrzlIuBTSduE1aOK/Zugf+AMlNbxekdYv
+8rhlCkO4lX752Dy8Z6x6KqRwRrXk+yeAhL7BITpsMCtJCR7XIsXKr9u/465tkjhC
+DlkqFhwk2I9uG0ygtkUkYpfakx0FBoroAQc3qAgQuQKBgQD+6BPb9nvvS403DcEh
+KNV0Pegdjms4KGiZmhOskWPOmMVKyIUHULgcQiwoROPzSfHjHABRuaVUz2+n37Kx
+kU9h+FTolIQUD/S9aYwEIf83Eu7CGj9ictLUPeLxbGSJ6B5+74D8RuOj5jS2VNug
+kJeBl0OWgD7ZXJF3LotSmnyiuwKBgQDeHRPaaBqp+5rZOLghyztsXnXQEcvMl5pt
+wUnm77XMfDIJf+KDKdOA7avelcMsuv1UDQNpckshaeU6W5UYrw6CI/gpB5JHJBjQ
+Bs6kTEw73t0+B1C9X/MA/AHIbk4TYLluxrv8pCprAptACuvEyHX+d03lETw+slzD
+mLWDMnqIHwKBgQCDbifnL1TBkkPyke31afd9IvpOWwNfhj5AjJf+T0yV1mFLaM5m
+cjErqNbZwIOECqlkfyiO+tiLPRWGCio5sgGrMv6cmQ7sxTlcfFJMQzczL1jZzezG
+lwurkWk1L706+erXaigoa2iuNmERbfl79XGYyOR5chB1xGvgdqgxZCRFRwKBgDxz
+s4yVGvS6uwl2C39/Hdw/1VbdERfNB0Xp/qAxC8zs3H2DZfYG8z668TUyk51gA0TW
+CeCwL8yXUsFQXcMLGirHeWpJWkGsjGhKAgHrljARVyvjt9DjBXN2I1IW238gqzeA
+NXfsgnL/kZubnVHAsYShmfzHdRsnVmIR9Q0RNxJHAoGBAMfqM6egc5JRSmDhA408
+d/oKasqYuPfNKWjZNfmDJcs1yHjaCJdQMkLlAi7a9QTi05KfEJGFf/k+ai7OIInw
+TCgworaS8+y/1YihDIMGHfFF2B3IYEJQkEIDHXUBaVA9xMXgYLBDYAme82riZia2
+9mDJn1VPU0HRotyDYwoC34JV
+-----END PRIVATE KEY-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt
new file mode 100644
index 000000000..fe9c08f48
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUTCCAjmgAwIBAgIULehsb6UOfWOosQs9hh0u0GJfIUcwDQYJKoZIhvcNAQEL
+BQAwGDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyMzlaFw0y
+OTExMDMxNzIyMzlaMBgxFjAUBgNVBAMMDVNlbnN1LXRlc3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXGeaaBh1Ysfd+YWbOHrlb9fIb39wp/Xj3
+8JvvdcTvEu9IvDrFASUYuyDo/y8s1XGMulzjKO1BzJr7NUTcMfvxMrXlKQFxmPWS
+KxNVlPerBgiY8013ETtx1v91rq6K2tX2s1GnQOJ2+MWBHLpLRqa69l2XtIxR33+u
+53T/vJAZVxzXU7pSfbRA7EvvaUry4IdCVhf8GCgvYopfZrxzByJg0fVgLNfbAlXb
+7bBFnpbVabseAaL+jKeikejK1wy2DgcNvfTxZRc8V5MohyQKepgbIYegrmFx9gKg
+vRtn/neGBFv3KndlogNZrYkH6qJk9Qkgjm4ZxgylGgnKiT/9Bn07AgMBAAGjgZIw
+gY8wHQYDVR0OBBYEFGVvNX0RCwv9O8/mO5X6NWuADSfpMFMGA1UdIwRMMEqAFGVv
+NX0RCwv9O8/mO5X6NWuADSfpoRykGjAYMRYwFAYDVQQDDA1TZW5zdS10ZXN0IENB
+ghQt6GxvpQ59Y6ixCz2GHS7QYl8hRzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB
+BjANBgkqhkiG9w0BAQsFAAOCAQEAP01K867avDUQpSsdhKONcZ/QHpL7ao/cO+in
+NbjQ9BEvV0Zjiw+xhlNy2U3G1UhbwOgCTIs6QLSA9sQyl3uAgXua9VmVH2bHT84m
+tRS6K0olAw5xAMwCH6Nf6wTBABDk4O4ny1XYFRpMYsSrvk1S1LBGSwLuDVHIb+3K
+3yOxCIROFsPy00+CimqXS3GwpFBerXXCAH7Rq8dyCMC1anXMX8YJz5WrpKW9wCrQ
+RtnuWLImCu5w9Oe0QtKKZmh3wJD6uBo7gxX8ZugtrpZcZMUpo6DS1KsiKOTnibCJ
+6k+A09PhNoorVrNnw/xmWgLf0J3CBE2jxUlq57QPYcVnFSs39w==
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt
new file mode 100644
index 000000000..68363428f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.crt
@@ -0,0 +1,87 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ a0:13:00:67:13:31:d4:25:47:fa:1a:48:2b:76:a9:7f
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: CN=Sensu-test CA
+ Validity
+ Not Before: Nov 6 17:22:42 2019 GMT
+ Not After : Oct 21 17:22:42 2022 GMT
+ Subject: CN=sensu-api
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:c4:16:71:d3:3b:08:26:1e:81:60:96:6e:9c:75:
+ ec:58:f2:dc:8f:22:e7:c8:64:b9:52:55:11:51:d5:
+ eb:d7:ae:7f:48:81:25:ea:4e:66:33:ee:be:73:25:
+ 84:ed:02:a6:f5:5f:d4:c0:2b:a6:d3:ae:c7:41:64:
+ 2c:5e:4b:6c:12:92:64:53:41:4a:59:b6:ef:d2:88:
+ 24:96:26:30:e3:98:25:45:c6:59:c4:e6:33:2c:a2:
+ 38:25:ce:f5:d5:d2:ba:39:68:1b:ec:f1:77:e6:e1:
+ 20:19:40:e5:ed:8c:bc:44:40:ce:0c:26:7d:86:60:
+ 0d:19:1f:31:59:85:a5:6a:d2:41:c8:1c:d6:7e:7b:
+ d1:1f:d4:35:2e:c2:c8:f6:da:03:39:54:0e:e6:8c:
+ 1b:26:e4:e6:3c:1f:88:64:c7:4f:46:89:70:6c:5b:
+ 26:ee:f2:a3:26:93:ac:00:4f:5d:d3:8b:5f:19:cc:
+ 53:f8:68:6b:61:fe:20:ba:b5:af:8a:9c:04:ea:bd:
+ 25:2c:e5:bf:7d:89:f3:d0:8d:90:79:7e:62:0e:88:
+ 47:cc:cf:94:26:00:76:c9:c1:93:85:a5:1b:19:5e:
+ 34:73:3a:eb:23:72:23:03:d6:83:29:26:4c:5d:4f:
+ ad:7d:1c:1f:e7:85:c3:d4:4e:0a:b2:1f:68:c4:67:
+ 19:c3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ E8:97:A6:14:2C:C4:75:73:3A:9B:AD:8D:91:3E:1D:6C:7E:C0:C1:9D
+ X509v3 Authority Key Identifier:
+ keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9
+ DirName:/CN=Sensu-test CA
+ serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ X509v3 Subject Alternative Name:
+ IP Address:176.16.117.105
+ Signature Algorithm: sha256WithRSAEncryption
+ 13:92:90:3f:2e:08:d3:e7:ce:77:4d:fe:db:d0:75:ed:4f:ca:
+ ab:6e:2a:8c:6d:05:1d:b2:d0:be:2f:ab:f5:ff:84:71:e0:24:
+ 93:92:89:2a:e4:14:ef:1c:dc:d3:e7:f1:6e:44:cc:35:b9:90:
+ 9b:c4:c3:18:64:97:bb:70:67:22:59:94:cd:c0:f3:f2:85:7c:
+ f6:d0:97:e2:f5:4a:fa:6a:0a:94:19:b3:de:90:84:1c:49:12:
+ 9c:f7:29:93:51:07:9f:84:61:36:19:86:02:6e:7f:8f:1c:e2:
+ 86:93:8b:cb:04:f4:54:b6:9b:22:ee:c3:bb:ab:6f:7d:f8:ae:
+ 9e:6b:71:e1:f7:4b:73:21:a2:f4:d3:44:1e:20:f5:55:10:9b:
+ ce:24:33:24:5f:21:a3:d3:75:c3:76:0d:b5:35:b3:95:d9:da:
+ 97:ed:15:9e:3e:6e:df:0f:a3:2a:10:e4:f9:49:b8:b2:f2:b2:
+ 99:e3:99:12:88:20:5c:2f:ad:1a:d6:e7:f1:29:e4:4d:e3:fb:
+ 30:72:1a:1b:53:69:ac:40:f7:a5:84:8d:12:e9:6f:78:a6:b9:
+ c0:b5:28:28:64:88:c9:da:69:d0:8e:6b:e9:7c:56:9f:68:ad:
+ 09:76:e0:a3:26:21:86:65:ff:7b:15:89:12:41:83:52:04:5d:
+ 61:b4:88:25
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIRAKATAGcTMdQlR/oaSCt2qX8wDQYJKoZIhvcNAQELBQAw
+GDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyNDJaFw0yMjEw
+MjExNzIyNDJaMBQxEjAQBgNVBAMMCXNlbnN1LWFwaTCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBAMQWcdM7CCYegWCWbpx17Fjy3I8i58hkuVJVEVHV69eu
+f0iBJepOZjPuvnMlhO0CpvVf1MArptOux0FkLF5LbBKSZFNBSlm279KIJJYmMOOY
+JUXGWcTmMyyiOCXO9dXSujloG+zxd+bhIBlA5e2MvERAzgwmfYZgDRkfMVmFpWrS
+Qcgc1n570R/UNS7CyPbaAzlUDuaMGybk5jwfiGTHT0aJcGxbJu7yoyaTrABPXdOL
+XxnMU/hoa2H+ILq1r4qcBOq9JSzlv32J89CNkHl+Yg6IR8zPlCYAdsnBk4WlGxle
+NHM66yNyIwPWgykmTF1PrX0cH+eFw9ROCrIfaMRnGcMCAwEAAaOBtTCBsjAJBgNV
+HRMEAjAAMB0GA1UdDgQWBBTol6YULMR1czqbrY2RPh1sfsDBnTBTBgNVHSMETDBK
+gBRlbzV9EQsL/TvP5juV+jVrgA0n6aEcpBowGDEWMBQGA1UEAwwNU2Vuc3UtdGVz
+dCBDQYIULehsb6UOfWOosQs9hh0u0GJfIUcwEwYDVR0lBAwwCgYIKwYBBQUHAwEw
+CwYDVR0PBAQDAgWgMA8GA1UdEQQIMAaHBLAQdWkwDQYJKoZIhvcNAQELBQADggEB
+ABOSkD8uCNPnzndN/tvQde1PyqtuKoxtBR2y0L4vq/X/hHHgJJOSiSrkFO8c3NPn
+8W5EzDW5kJvEwxhkl7twZyJZlM3A8/KFfPbQl+L1SvpqCpQZs96QhBxJEpz3KZNR
+B5+EYTYZhgJuf48c4oaTi8sE9FS2myLuw7urb334rp5rceH3S3MhovTTRB4g9VUQ
+m84kMyRfIaPTdcN2DbU1s5XZ2pftFZ4+bt8PoyoQ5PlJuLLyspnjmRKIIFwvrRrW
+5/Ep5E3j+zByGhtTaaxA96WEjRLpb3imucC1KChkiMnaadCOa+l8Vp9orQl24KMm
+IYZl/3sViRJBg1IEXWG0iCU=
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key
new file mode 100644
index 000000000..e5854027d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-api.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDEFnHTOwgmHoFg
+lm6cdexY8tyPIufIZLlSVRFR1evXrn9IgSXqTmYz7r5zJYTtAqb1X9TAK6bTrsdB
+ZCxeS2wSkmRTQUpZtu/SiCSWJjDjmCVFxlnE5jMsojglzvXV0ro5aBvs8Xfm4SAZ
+QOXtjLxEQM4MJn2GYA0ZHzFZhaVq0kHIHNZ+e9Ef1DUuwsj22gM5VA7mjBsm5OY8
+H4hkx09GiXBsWybu8qMmk6wAT13Ti18ZzFP4aGth/iC6ta+KnATqvSUs5b99ifPQ
+jZB5fmIOiEfMz5QmAHbJwZOFpRsZXjRzOusjciMD1oMpJkxdT619HB/nhcPUTgqy
+H2jEZxnDAgMBAAECggEAK8zTqhpCjLk9rwSLOpnArHG7QKHMYl/VYYWs87m0D55j
+wh9PB9JxU+JdWj0kPwjboG3CiRZ3Eku1KG8m1f1E67UVgd9Qq0+IrF9KxNtNClme
+4cIXpTrCbZLitddP5G5IuK//pOKfJMxeriVn9rL8Dsbm/6HNYimsY1MrY9LNi1l9
+zAxSAihUPI0JM0DEA+5C4vaIFiOt1ulhTXuRU94v8gjcDMnpp/rB8pP0XvpjNdzb
+7LExeMrywEOnxGEju1Co5Q5jp2epDwjSowlTg+wJCu0dlCW6uWGV0umW+KwK+aQ0
+Z/Mo4VwE4kRO/2vth9N0tboHOL5EoDdKprk1v5E1+QKBgQDi8nqgaDlfsO4HIGPE
+NiQvevxQBQ7D2vElt04fYpla4iTJpmXeo35194sW40pAbt2N3/xsEAFZPGWiGWVe
+km8tLdF2vHHNp4wFHEjb86hu6h07XAikr+1+ZI+musn09lgH8gXmy610PJ+OLHnf
+Rmkfhr73EhBa2PLp4GArwq6D/wKBgQDdMKL/SkdwCUqz3Z3UP2cRBBNi0JDXCGTb
+jpQMvJZtrosxR9su55w5ZtIKLRPn7fR0AfRztBKUQBmEYgcp+1VkhIA5v6GGzNAj
+af/dVZhyXV9OPdSc5pDk+bWbrj8GxQr/5C04clVnwZ2ESNm8tulpwo2MZPPoj/vk
+tuN7UZZaPQKBgQCAZLhVicFzzrBLXdqzhgHgzs5yIvpgebxWHydWgDzMewZfAwG8
+/HguGzcYYsx+OXqkqmSvajqpFo9VLtL3txao07QeXaxwsep4dbEOpwHShia1j3Lg
+YRuWlyPiKujY6omRLS6DjRV7nlSSZb0pQTd1+5CMTS7thrGe+S7PcxuyVwKBgBgK
+WDztAtSvfdoMxUGzXm1gBwdfac6lT+j5FyhHOwZSyTgi+jSf4b/vZ/bJLXewyjft
+mncU5EwOp3dW/DZY5dAWAqXEKTcwfZLLy45v5jDP6zLiz3/6I1dvuIhiKOGAexCS
+6UNQUe4EAi3FiTzUmIvxJFdVBZmKRLN4GUNm+7N1AoGAA9VfdyWQYTu+z0y91/ob
+y77j0hiG+A7HK7PNW2ZIwnhjczxuQ7Dcpk/GIqnOamrslLMNRU9zNETPRdEkoMnX
+I8ZyrGFKCby9FNBHpgbDFjY7+EgaoWwVjt4UhbZOQQAVVkoBS3CUSMFxs29iAun1
+uqY0wSO5KtVggDqqcZJpkHw=
+-----END PRIVATE KEY-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt
new file mode 100644
index 000000000..8cc61dada
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt
@@ -0,0 +1,87 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 9a:76:0c:19:84:14:e0:bf:39:06:f1:25:83:09:32:fc
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: CN=Sensu-test CA
+ Validity
+ Not Before: Nov 6 17:22:42 2019 GMT
+ Not After : Oct 21 17:22:42 2022 GMT
+ Subject: CN=sensu-dashboard
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:b0:e0:10:ec:e6:50:88:db:f6:78:ba:21:a4:c4:
+ 17:b5:f0:57:9b:54:da:4d:1f:75:d2:da:15:4f:17:
+ 1f:d7:cd:15:0e:1e:39:7c:50:6a:32:11:11:9c:76:
+ 01:77:8e:a4:07:49:55:49:8e:96:1a:c7:a5:90:3e:
+ 69:4d:ec:a9:45:a3:07:05:89:7a:dd:a6:34:cb:29:
+ c8:36:71:ba:b6:ec:d8:20:60:58:5f:23:ca:61:78:
+ e1:ca:a5:6a:b6:ea:d9:dd:4f:ce:fb:2e:9c:9d:06:
+ eb:de:d9:be:58:d3:a8:da:2b:2d:4f:f9:7a:32:43:
+ 52:68:60:41:e5:b7:65:c7:4d:ec:ae:56:99:b1:05:
+ 66:5c:77:03:41:28:e1:b9:75:7f:67:3c:09:e7:d3:
+ 4d:f2:77:51:74:2c:8b:82:ab:5f:97:ea:c0:f7:45:
+ e5:85:35:a2:5c:95:65:79:a1:dd:bd:bf:a6:e8:76:
+ ae:36:c7:36:7c:e7:d9:e8:57:65:e2:3f:ea:0b:8d:
+ 16:61:3c:c9:3b:89:3d:a7:cf:5a:09:4c:f2:98:03:
+ fd:6d:d0:20:8c:12:4c:a3:38:af:53:71:75:54:30:
+ f9:e8:d3:44:4e:f2:fc:b8:ec:f9:49:ff:c8:bc:27:
+ 00:b2:2c:c6:15:05:b4:5b:f7:ac:20:fc:df:b5:56:
+ 26:e3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 08:7A:07:19:64:FD:EA:87:C3:25:FE:65:E6:8B:A7:9E:E2:46:8C:A7
+ X509v3 Authority Key Identifier:
+ keyid:65:6F:35:7D:11:0B:0B:FD:3B:CF:E6:3B:95:FA:35:6B:80:0D:27:E9
+ DirName:/CN=Sensu-test CA
+ serial:2D:E8:6C:6F:A5:0E:7D:63:A8:B1:0B:3D:86:1D:2E:D0:62:5F:21:47
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ X509v3 Subject Alternative Name:
+ IP Address:176.16.117.105
+ Signature Algorithm: sha256WithRSAEncryption
+ 47:ca:52:10:7a:01:5e:e3:0d:ae:e7:5e:a7:f8:19:d2:0e:0a:
+ 03:ef:9f:dc:2f:4f:94:05:15:ae:ee:66:6a:7c:41:cc:07:8f:
+ 75:09:8c:be:e6:ea:8c:1c:f2:d5:20:23:76:b1:cb:ec:da:1e:
+ 07:83:23:3b:ce:62:07:65:1f:49:26:f5:59:50:d0:2b:b2:5f:
+ 61:9d:6d:6b:1f:e4:8d:60:ae:c2:fd:02:40:38:4f:6c:d3:48:
+ 5c:ca:4e:ac:70:88:63:57:21:f8:47:57:be:d9:bb:ec:ce:e4:
+ ac:be:81:f7:21:60:95:6d:56:b9:ce:93:8b:f5:3f:c9:de:77:
+ 3b:34:9a:73:d1:9a:99:ea:35:47:ef:fb:01:48:98:6d:f9:dd:
+ d2:16:7e:f2:d5:17:d0:66:c7:b1:2b:55:18:80:f7:7d:5d:d3:
+ 1b:11:18:ef:1d:e4:35:a5:7d:25:a8:b3:cd:80:5c:7e:95:4d:
+ 9b:79:e1:b0:38:84:75:58:f1:45:4f:48:ed:8b:84:9b:a6:26:
+ a0:f6:d1:f0:f7:f2:1c:e7:47:ee:b3:30:a9:61:82:4b:fc:d9:
+ dc:45:ab:2c:f4:30:60:19:0e:92:cc:67:d8:3b:6e:26:80:fb:
+ b3:3c:21:4e:66:45:fe:05:55:6a:88:04:74:85:ca:1a:75:70:
+ 38:0f:ca:d4
+-----BEGIN CERTIFICATE-----
+MIIDczCCAlugAwIBAgIRAJp2DBmEFOC/OQbxJYMJMvwwDQYJKoZIhvcNAQELBQAw
+GDEWMBQGA1UEAwwNU2Vuc3UtdGVzdCBDQTAeFw0xOTExMDYxNzIyNDJaFw0yMjEw
+MjExNzIyNDJaMBoxGDAWBgNVBAMMD3NlbnN1LWRhc2hib2FyZDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBALDgEOzmUIjb9ni6IaTEF7XwV5tU2k0fddLa
+FU8XH9fNFQ4eOXxQajIREZx2AXeOpAdJVUmOlhrHpZA+aU3sqUWjBwWJet2mNMsp
+yDZxurbs2CBgWF8jymF44cqlarbq2d1PzvsunJ0G697ZvljTqNorLU/5ejJDUmhg
+QeW3ZcdN7K5WmbEFZlx3A0Eo4bl1f2c8CefTTfJ3UXQsi4KrX5fqwPdF5YU1olyV
+ZXmh3b2/puh2rjbHNnzn2ehXZeI/6guNFmE8yTuJPafPWglM8pgD/W3QIIwSTKM4
+r1NxdVQw+ejTRE7y/Ljs+Un/yLwnALIsxhUFtFv3rCD837VWJuMCAwEAAaOBtTCB
+sjAJBgNVHRMEAjAAMB0GA1UdDgQWBBQIegcZZP3qh8Ml/mXmi6ee4kaMpzBTBgNV
+HSMETDBKgBRlbzV9EQsL/TvP5juV+jVrgA0n6aEcpBowGDEWMBQGA1UEAwwNU2Vu
+c3UtdGVzdCBDQYIULehsb6UOfWOosQs9hh0u0GJfIUcwEwYDVR0lBAwwCgYIKwYB
+BQUHAwEwCwYDVR0PBAQDAgWgMA8GA1UdEQQIMAaHBLAQdWkwDQYJKoZIhvcNAQEL
+BQADggEBAEfKUhB6AV7jDa7nXqf4GdIOCgPvn9wvT5QFFa7uZmp8QcwHj3UJjL7m
+6owc8tUgI3axy+zaHgeDIzvOYgdlH0km9VlQ0CuyX2GdbWsf5I1grsL9AkA4T2zT
+SFzKTqxwiGNXIfhHV77Zu+zO5Ky+gfchYJVtVrnOk4v1P8nedzs0mnPRmpnqNUfv
++wFImG353dIWfvLVF9Bmx7ErVRiA931d0xsRGO8d5DWlfSWos82AXH6VTZt54bA4
+hHVY8UVPSO2LhJumJqD20fD38hznR+6zMKlhgkv82dxFqyz0MGAZDpLMZ9g7biaA
++7M8IU5mRf4FVWqIBHSFyhp1cDgPytQ=
+-----END CERTIFICATE-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key
new file mode 100644
index 000000000..da9a9f952
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCw4BDs5lCI2/Z4
+uiGkxBe18FebVNpNH3XS2hVPFx/XzRUOHjl8UGoyERGcdgF3jqQHSVVJjpYax6WQ
+PmlN7KlFowcFiXrdpjTLKcg2cbq27NggYFhfI8pheOHKpWq26tndT877LpydBuve
+2b5Y06jaKy1P+XoyQ1JoYEHlt2XHTeyuVpmxBWZcdwNBKOG5dX9nPAnn003yd1F0
+LIuCq1+X6sD3ReWFNaJclWV5od29v6bodq42xzZ859noV2XiP+oLjRZhPMk7iT2n
+z1oJTPKYA/1t0CCMEkyjOK9TcXVUMPno00RO8vy47PlJ/8i8JwCyLMYVBbRb96wg
+/N+1VibjAgMBAAECggEBAKrHTdBO+MeMCsi6fy2FoJcs/omePkFk9PCnXRfGbhqB
+i6jcCgk7461/yY9WaUg006+tYMnrAIfO6M8DU83HihEQvgDco2NLzYG0T/oxiWSB
+/pQUMn96IThH7UsquITw8Xa8Tk88zD7ZpfjAKQe/JjOwqMmlShUp53GcL0RL9due
+huI8QBzbiKvBUMME1A8yaNbyy6BwHBjxfJHXaSeNwmUnlKS0A/jMNKtav8jCfdnt
+QEuBdL4kexZf/lt+Mu6PmODoeT8VZRrxEf4v79MZxJZNTLOVUjIhELQF1UESkbqI
+RoU1vVzU7CsbOWHAaym7ShZK3QDzfnVLkhFAEwsRPsECgYEA6n+SULpYTR7CtBlw
+zVSPePe8CiA2W28VoQrXk/9FY/j1o2cGyHidl0mVuagIROW+LyCv+HQxinJzSQrK
+AW/aoPgrUUfkTbpmxrPvM5FszG5cVkdFYMsW2Ni/CY8LCRdUReICdEl5xDMMfB2e
+/yM6oNFh7LLNrSwUsGlifJ7OcPMCgYEAwRflbOzB98lV6guQJRQISqO4+9OCIXJL
+ZDoHxaJ9TVr3cEFUYfm7gFvtgn2QFlqgfCN1omY3vlTuYp4llFw/Z4NX9F673erU
+jtt26C1pxmC7kdGApupz+gVWSqO4WhOSgpEUErUX9aX+YEy6UmdzkHnb/1cyT1eG
+lVvz2/eUblECgYA9aw5agIQSJuVeIG+wB97QEyq4CDnUduLWXC2cgLae+Zz0oE5h
+gV3dOxOxHbaUvQuz8j7Et0ImfdV+IwpHmBFOKdHGpyq/xPuYPZaADi3N2XXrzxz3
+vhmM0DAxA7sjNW4II6r65Ce1YJ17gJKdRo/bgRvB0A8YtTvx/JgkBcASSwKBgBzx
+2BJb6zeZlqde1Fy6hAOsRy54pikdWO/NQxz9HotZ9318TYniRZkYLqJA8DhpnWT+
+a8PMTs7ZLGLcEgYLTfXWWnjnOoIpkXNYsppbNF/oYDWbkg1zV69C3YySvi/Cf1PT
+K48iVlUcbOVCmyt/FnOx0KiWCZSbKjF5dzSiCD4BAoGAeWRARbEkptVmG1iBdhU1
+SNy1WhNU2L6TfUEbevYWWElQYZiQmwR4XvOfRaR3Dx8pTicABqcAwLgYPbqzCknu
+QcPULpLU/EJKP9xRhZClJVSJZ7JRHQrZt/i0WRVuTCCtafp50dns0vMYWBF4vVrE
+KUa5kMVzh3unaWHUCJ7qODw=
+-----END PRIVATE KEY-----
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml
new file mode 100644
index 000000000..9d526ef36
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/molecule.yml
@@ -0,0 +1,18 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - prepare
+ - converge
+ - destroy
+
+platforms:
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
+ override_command: false
+ privileged: true
+ volumes:
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml
new file mode 100644
index 000000000..40ba62238
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_backend_secured/prepare.yml
@@ -0,0 +1,24 @@
+---
+- name: Prepare
+ hosts: all
+
+ tasks:
+ - name: Create sensu group
+ group:
+ name: sensu
+
+ - name: Create sensu user
+ # We need FQCN here because we are running test from within the
+ # collection. In this case, our collection becomes the default
+ # collection and so the sensu.sensu_go.user module shadows the builtin
+ # one.
+ ansible.builtin.user:
+ name: sensu
+ groups: sensu
+
+ - name: Create /etc/sensu folder
+ file:
+ state: directory
+ path: /etc/sensu
+ owner: sensu
+ group: sensu
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml
new file mode 100644
index 000000000..0aa2bf12a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/converge.yml
@@ -0,0 +1,50 @@
+---
+- name: Test different version builds
+ hosts: all
+ tasks:
+ - name: Install a specific build
+ include_role:
+ name: sensu.sensu_go.install
+ vars:
+ components: [sensu-go-backend]
+ channel: testing
+ version: 5.16.0
+ build: 8290
+
+ - package_facts:
+ manager: auto
+
+ - assert:
+ that:
+ - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0-8290"
+ when: ansible_facts.packages["sensu-go-backend"][0].source == "apt"
+
+ - assert:
+ that:
+ - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0"
+ - ansible_facts.packages["sensu-go-backend"][0].release == "8290"
+ when: ansible_facts.packages["sensu-go-backend"][0].source == "rpm"
+
+ - name: Update to a specific build
+ include_role:
+ name: sensu.sensu_go.install
+ tasks_from: packages
+ vars:
+ components: [sensu-go-backend]
+ channel: testing
+ version: 5.16.0
+ build: 8320
+
+ - package_facts:
+ manager: auto
+
+ - assert:
+ that:
+ - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0-8320"
+ when: ansible_facts.packages["sensu-go-backend"][0].source == "apt"
+
+ - assert:
+ that:
+ - ansible_facts.packages["sensu-go-backend"][0].version == "5.16.0"
+ - ansible_facts.packages["sensu-go-backend"][0].release == "8320"
+ when: ansible_facts.packages["sensu-go-backend"][0].source == "rpm"
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml
new file mode 100644
index 000000000..59e497b6b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_build/molecule.yml
@@ -0,0 +1,11 @@
+---
+platforms:
+ - name: ubuntu
+ image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:16.04
+ pre_build_image: true
+ pull: true
+
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml
new file mode 100644
index 000000000..70be5856e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/converge.yml
@@ -0,0 +1,31 @@
+---
+- name: Converge with older versions of components
+ hosts: all
+
+ tasks:
+ - name: Install all components
+ include_role:
+ name: sensu.sensu_go.install
+ vars:
+ components:
+ - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
+ version: 5.14.1
+
+ - name: Make sure components are installed
+ command:
+ cmd: "{{ item }} version"
+ loop:
+ - sensu-backend
+ - sensu-agent
+ - sensuctl
+ register: result
+
+ - assert:
+ quiet: true
+ that:
+ - item.stdout is search("5.14.1")
+ loop: "{{ result.results }}"
+ loop_control:
+ label: "{{ item.item }}" # Reduce verbosity a bit
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml
new file mode 100644
index 000000000..59e497b6b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_custom_version/molecule.yml
@@ -0,0 +1,11 @@
+---
+platforms:
+ - name: ubuntu
+ image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:16.04
+ pre_build_image: true
+ pull: true
+
+ - name: centos
+ image: quay.io/xlab-steampunk/sensu-go-tests-centos:7
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml
new file mode 100644
index 000000000..7ac2dd838
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/converge.yml
@@ -0,0 +1,13 @@
+---
+- name: Converge with latest versions of components
+ hosts: all
+
+ tasks:
+ - name: Install all components
+ include_role:
+ name: sensu.sensu_go.install
+ vars:
+ components:
+ - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml
new file mode 100644
index 000000000..1261bc9d0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/molecule.yml
@@ -0,0 +1,35 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - converge
+ - verify
+ - check
+ - destroy
+
+platforms:
+ - name: debian-9
+ image: quay.io/xlab-steampunk/sensu-go-tests-debian:9
+ pre_build_image: true
+ pull: true
+
+ - name: debian-10
+ image: quay.io/xlab-steampunk/sensu-go-tests-debian:10
+ pre_build_image: true
+ pull: true
+
+ - name: ubuntu-14.04
+ image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:14.04
+ pre_build_image: true
+ pull: true
+
+ - name: ubuntu-16.04
+ image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:16.04
+ pre_build_image: true
+ pull: true
+
+ - name: ubuntu-18.04
+ image: quay.io/xlab-steampunk/sensu-go-tests-ubuntu:18.04
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml
new file mode 100644
index 000000000..8dadfc5ef
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_deb/verify.yml
@@ -0,0 +1,12 @@
+---
+- name: Verify
+ hosts: all
+
+ tasks:
+ - name: Make sure components are installed
+ command:
+ cmd: "{{ item }} version"
+ loop:
+ - sensu-backend
+ - sensu-agent
+ - sensuctl
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml
new file mode 100644
index 000000000..7ac2dd838
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/converge.yml
@@ -0,0 +1,13 @@
+---
+- name: Converge with latest versions of components
+ hosts: all
+
+ tasks:
+ - name: Install all components
+ include_role:
+ name: sensu.sensu_go.install
+ vars:
+ components:
+ - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml
new file mode 100644
index 000000000..a0d8b5e6c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/molecule.yml
@@ -0,0 +1,30 @@
+---
+scenario:
+ test_sequence:
+ - destroy
+ - create
+ - converge
+ - verify
+ - check
+ - destroy
+
+platforms:
+ - name: redhat-7
+ image: quay.io/xlab-steampunk/sensu-go-tests-redhat:7
+ pre_build_image: true
+ pull: true
+
+ - name: amazon-1
+ image: quay.io/xlab-steampunk/sensu-go-tests-amazon:1
+ pre_build_image: true
+ pull: true
+
+ - name: amazon-2
+ image: quay.io/xlab-steampunk/sensu-go-tests-amazon:2
+ pre_build_image: true
+ pull: true
+
+ - name: oracle-8
+ image: quay.io/xlab-steampunk/sensu-go-tests-oracle:8
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml
new file mode 100644
index 000000000..8dadfc5ef
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_default_rpm/verify.yml
@@ -0,0 +1,12 @@
+---
+- name: Verify
+ hosts: all
+
+ tasks:
+ - name: Make sure components are installed
+ command:
+ cmd: "{{ item }} version"
+ loop:
+ - sensu-backend
+ - sensu-agent
+ - sensuctl
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml
new file mode 100644
index 000000000..b498ee945
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/converge.yml
@@ -0,0 +1,39 @@
+---
+- name: Test downgrade
+ hosts: all
+
+ tasks:
+ - name: Install initial version components
+ include_role:
+ name: sensu.sensu_go.install
+ vars:
+ components:
+ - sensu-go-agent
+ version: 6.2.5
+
+ - name: Make sure components are installed
+ command:
+ cmd: sensu-agent version
+ register: result
+ - assert:
+ # quiet: true
+ that:
+ - result.stdout is search("6.2.5")
+
+ - name: Downgrade components
+ include_role:
+ name: sensu.sensu_go.install
+ tasks_from: packages
+ vars:
+ components:
+ - sensu-go-agent
+ version: 6.1.4
+
+ - name: Make sure components were downgraded
+ command:
+ cmd: sensu-agent version
+ register: result
+ - assert:
+ quiet: true
+ that:
+ - result.stdout is search("6.1.4")
diff --git a/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml
new file mode 100644
index 000000000..c276ce556
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/integration/molecule/role_install_downgrade/molecule.yml
@@ -0,0 +1,13 @@
+---
+platforms:
+ # yum test
+ - name: redhat-7
+ image: quay.io/xlab-steampunk/sensu-go-tests-redhat:7
+ pre_build_image: true
+ pull: true
+
+ # apt test
+ - name: debian-10
+ image: quay.io/xlab-steampunk/sensu-go-tests-debian:10
+ pre_build_image: true
+ pull: true
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt
new file mode 100644
index 000000000..af9fcfb5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.10.txt
@@ -0,0 +1,4 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt
new file mode 100644
index 000000000..af9fcfb5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.11.txt
@@ -0,0 +1,4 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt
new file mode 100644
index 000000000..af9fcfb5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.12.txt
@@ -0,0 +1,4 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt
new file mode 100644
index 000000000..af9fcfb5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.13.txt
@@ -0,0 +1,4 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt
new file mode 100644
index 000000000..af9fcfb5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.14.txt
@@ -0,0 +1,4 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt
new file mode 100644
index 000000000..af9fcfb5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.15.txt
@@ -0,0 +1,4 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tests/unit/plugins/module_utils/test_utils.py pylint:ansible-deprecated-no-collection-name # sanity misdetects this as module deprecation call
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt
new file mode 100644
index 000000000..5a7ce7659
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/ignore-2.9.txt
@@ -0,0 +1,3 @@
+plugins/modules/bonsai_asset.py validate-modules:nonexistent-parameter-documented # This is not a real module, more helper for the asset module
+tools/windows-versions.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
+tests/sanity/validate-role-metadata.py replace-urlopen # Maintainer tools should not be bound by the general collection constraints
diff --git a/ansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py b/ansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py
new file mode 100755
index 000000000..b8544ff9e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/sanity/validate-role-metadata.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import argparse
+import json
+import os
+import sys
+
+import yaml
+
+import urllib.request
+
+
+def _get_arg_parser():
+ parser = argparse.ArgumentParser(description="Validate role metadata")
+ parser.add_argument("role", nargs="+", help="role path")
+ return parser
+
+
+def _validate_role_platforms(platforms):
+ base_url = "https://galaxy.ansible.com/api/v1/platforms/?name={0}&release={1}"
+ msgs = []
+ for platform in platforms:
+ for release in platform["versions"]:
+ url = base_url.format(platform["name"], release)
+ f = urllib.request.urlopen(url)
+
+ if len(json.loads(f.read().decode('utf-8'))["results"]) != 1:
+ msgs.append(("ERROR", "Invalid platform '{0} {1}'".format(
+ platform["name"], release,
+ )))
+
+ return msgs
+
+
+def _validate_role(role_path):
+ meta_file = os.path.join(role_path, "meta", "main.yml")
+ with open(meta_file) as fd:
+ galaxy_info = yaml.safe_load(fd)["galaxy_info"]
+
+ msgs = []
+ msgs.extend(_validate_role_platforms(galaxy_info["platforms"]))
+
+ return msgs
+
+
+def main():
+ args = _get_arg_parser().parse_args()
+ no_msgs = 0
+ for role in args.role:
+ msgs = _validate_role(role)
+ for msg in msgs:
+ no_msgs += 1
+ print("{0}: {1}".format(*msg))
+
+ return 0 if no_msgs == 0 else 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py
new file mode 100644
index 000000000..100b30c6b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py
@@ -0,0 +1,316 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible.playbook.task import Task
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ bonsai, errors,
+)
+from ansible_collections.sensu.sensu_go.plugins.action import bonsai_asset
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestValidate:
+ @pytest.mark.parametrize("name,args,required,typ", [
+ # Required values must match the selected type.
+ ("a", dict(a=3), True, int),
+ ("a", dict(a=3.3), True, float),
+ ("a", dict(a="b"), True, str),
+ ("a", dict(a=[]), True, list),
+ ("a", dict(a={}), True, dict),
+ # Optional values are not checked for type-correctness if they are
+ # missing.
+ ("a", dict(), False, int),
+ ("a", dict(), False, float),
+ ("a", dict(), False, str),
+ ("a", dict(), False, list),
+ ("a", dict(), False, dict),
+ ])
+ def test_valid_values(self, name, args, required, typ):
+ bonsai_asset.validate(name, args, required, typ)
+
+ def test_missing_required(self):
+ with pytest.raises(errors.Error, match="required"):
+ bonsai_asset.validate("a", {}, True, str)
+
+ def test_invalid_type(self):
+ with pytest.raises(errors.Error, match="should"):
+ bonsai_asset.validate("a", dict(a=3), True, str)
+
+ def test_invalid_type_for_optional_value(self):
+ with pytest.raises(errors.Error, match="should"):
+ bonsai_asset.validate("a", dict(a=3), False, dict)
+
+
+class TestValidateArguments:
+ def test_valid_minimal_args(self):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3",
+ ))
+
+ def test_valid_all_args(self):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", rename="def",
+ labels={}, annotations={},
+ ))
+
+ def test_valid_unicode_strings_python2(self):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name=u"abc", version=u"1.2.3", rename=u"def",
+ labels={}, annotations={},
+ ))
+
+ def test_invalid_name(self):
+ with pytest.raises(errors.Error, match="name"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name=1.234, version="1.2.3",
+ ))
+
+ def test_missing_name(self):
+ with pytest.raises(errors.Error, match="name"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ version="1.2.3",
+ ))
+
+ def test_invalid_version(self):
+ with pytest.raises(errors.Error, match="version"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version=1.2,
+ ))
+
+ def test_missing_version(self):
+ with pytest.raises(errors.Error, match="version"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc",
+ ))
+
+ def test_invalid_rename(self):
+ with pytest.raises(errors.Error, match="rename"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", rename=1,
+ ))
+
+ def test_invalid_labels(self):
+ with pytest.raises(errors.Error, match="labels"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", labels=1,
+ ))
+
+ def test_invalid_annotations(self):
+ with pytest.raises(errors.Error, match="annotations"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", annotations=1,
+ ))
+
+
+class TestBuildAssetArgs:
+ def test_no_additional_metadata(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(name="test/asset", version="1.2.3"),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[],
+ )
+
+ def test_bonsai_metadata_only(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(name="test/asset", version="1.2.3"),
+ dict(builds=[], labels=dict(a="b"), annotations=dict(c="d")),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[],
+ annotations=dict(c="d"),
+ labels=dict(a="b"),
+ )
+
+ def test_user_metadata_only(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(
+ name="test/asset",
+ version="1.2.3",
+ labels=dict(my="label"),
+ annotations=dict(my="annotation"),
+ ),
+ dict(builds=[1, 2, 3], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[1, 2, 3],
+ annotations=dict(my="annotation"),
+ labels=dict(my="label"),
+ )
+
+ def test_mixed_metadata(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(
+ name="test/asset",
+ version="1.2.3",
+ labels=dict(my="label"),
+ annotations=dict(my="annotation"),
+ ),
+ dict(builds=[], labels=dict(my="x", a="b"), annotations=dict(my="c")),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[],
+ annotations=dict(my="annotation"),
+ labels=dict(my="label", a="b"),
+ )
+
+ def test_rename(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(name="test/asset", version="1.2.3", rename="my-asset"),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="my-asset",
+ state="present",
+ builds=[],
+ )
+
+ def test_auth_passthrough(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(
+ auth=dict(url="http://localhost:1234"),
+ name="test/asset",
+ version="1.2.3",
+ ),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ auth=dict(url="http://localhost:1234"),
+ name="test/asset",
+ state="present",
+ builds=[],
+ )
+
+ def test_namespace_passthrough(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(namespace='default', name="test/asset", version="1.2.3"),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ namespace='default',
+ state="present",
+ builds=[],
+ )
+
+
+class TestDownloadAssetDefinition:
+ def get_mock_action(self, mocker, result):
+ action = bonsai_asset.ActionModule(
+ mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock(), loader=None,
+ templar=None, shared_loader_obj=None,
+ )
+ action._execute_module = mocker.MagicMock(return_value=result)
+ return action
+
+ def test_download_on_control_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.return_value = dict(sample="value")
+ action = self.get_mock_action(mocker, {})
+
+ result = action.download_asset_definition(
+ on_remote=False, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ assert result == dict(sample="value")
+ bonsai_params.assert_called_once()
+ action._execute_module.assert_not_called()
+
+ def test_fail_download_on_control_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.side_effect = errors.BonsaiError("Bonsai bad")
+ action = self.get_mock_action(mocker, {})
+
+ with pytest.raises(errors.Error, match="Bonsai bad"):
+ action.download_asset_definition(
+ on_remote=False, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ bonsai_params.assert_called_once()
+ action._execute_module.assert_not_called()
+
+ def test_download_on_target_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ action = self.get_mock_action(mocker, dict(asset="sample"))
+
+ result = action.download_asset_definition(
+ on_remote=True, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ assert result == "sample"
+ bonsai_params.assert_not_called()
+ action._execute_module.assert_called_once()
+
+ def test_fail_on_target_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ action = self.get_mock_action(mocker, dict(failed=True, msg="Bad err"))
+
+ with pytest.raises(errors.Error, match="Bad err"):
+ action.download_asset_definition(
+ on_remote=True, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ bonsai_params.assert_not_called()
+ action._execute_module.assert_called_once()
+
+
+class TestRun:
+ def test_success(self, mocker):
+ task = mocker.MagicMock(Task, async_val=0, args=dict(
+ name="test/asset",
+ version="1.2.3",
+ ))
+ action = bonsai_asset.ActionModule(
+ task, mocker.MagicMock(), mocker.MagicMock(), loader=None,
+ templar=None, shared_loader_obj=None,
+ )
+ action._execute_module = mocker.MagicMock(return_value=dict(a=3))
+ action.download_asset_definition = mocker.MagicMock(
+ return_value=dict(builds=[], labels=None, annotations=None),
+ )
+
+ result = action.run()
+
+ assert result == dict(a=3)
+
+ def test_fail(self, mocker):
+ task = mocker.MagicMock(Task, async_val=0, args=dict(
+ name="test/asset",
+ ))
+ action = bonsai_asset.ActionModule(
+ task, mocker.MagicMock(), mocker.MagicMock(), loader=None,
+ templar=None, shared_loader_obj=None,
+ )
+ action._execute_module = mocker.MagicMock(return_value=dict(a=3))
+ action.download_asset_definition = mocker.MagicMock(
+ return_value=dict(builds=[], labels=None, annotations=None),
+ )
+
+ result = action.run()
+
+ assert result["failed"] is True
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py
new file mode 100644
index 000000000..261080e3b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py
@@ -0,0 +1,54 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.filter import backends
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestBackends:
+ def test_backends_in_groups_no_ssl(self):
+ hostvars = {
+ "1.2.3.4": {"inventory_hostname": "1.2.3.4"},
+ "1.2.3.5": {"inventory_hostname": "1.2.3.5"},
+ "1.2.3.6": {"inventory_hostname": "1.2.3.6"},
+ "1.2.3.7": {"inventory_hostname": "1.2.3.7"},
+ }
+ groups = {"backends": ["1.2.3.4", "1.2.3.5"]}
+
+ assert backends.backends(hostvars, groups) == [
+ "ws://1.2.3.4:8081",
+ "ws://1.2.3.5:8081",
+ ]
+
+ def test_backends_in_groups_ssl(self):
+ hostvars = {
+ "1.2.3.4": {"inventory_hostname": "1.2.3.4"},
+ "1.2.3.5": {"inventory_hostname": "1.2.3.5"},
+ "1.2.3.6": {
+ "inventory_hostname": "1.2.3.6",
+ "api_key_file": "path/to/key.file",
+ },
+ "1.2.3.7": {"inventory_hostname": "1.2.3.7"},
+ }
+ groups = {"backends": ["1.2.3.6"]}
+
+ assert backends.backends(hostvars, groups) == ["wss://1.2.3.6:8081"]
+
+ def test_backends_not_in_groups(self):
+ hostvars = {
+ "1.2.3.4": {"inventory_hostname": "1.2.3.4"},
+ "1.2.3.5": {"inventory_hostname": "1.2.3.5"},
+ "1.2.3.6": {"inventory_hostname": "1.2.3.6"},
+ "1.2.3.7": {"inventory_hostname": "1.2.3.7"},
+ }
+ groups = {}
+
+ assert backends.backends(hostvars, groups) == []
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py
new file mode 100644
index 000000000..794727abf
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py
@@ -0,0 +1,58 @@
+# Copyright: (c) 2020, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.filter import package_name
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestPackageName:
+ def test_yum_latest_version(self):
+ assert "package" == package_name.package_name(
+ "yum", "package", "latest", "latest",
+ )
+
+ def test_yum_latest_build(self):
+ assert "package-123" == package_name.package_name(
+ "yum", "package", "123", "latest",
+ )
+
+ def test_yum_selected_build(self):
+ assert "package-123-456" == package_name.package_name(
+ "yum", "package", "123", "456",
+ )
+
+ def test_yum_ignore_build_if_latest_version(self):
+ assert "package" == package_name.package_name(
+ "yum", "package", "latest", "456",
+ )
+
+ def test_apt_latest_version(self):
+ assert "package" == package_name.package_name(
+ "apt", "package", "latest", "latest",
+ )
+
+ def test_apt_latest_build(self):
+ assert "package=123-*" == package_name.package_name(
+ "apt", "package", "123", "latest",
+ )
+
+ def test_apt_selected_build(self):
+ assert "package=123-456" == package_name.package_name(
+ "apt", "package", "123", "456",
+ )
+
+ def test_apt_ignore_build_if_latest_version(self):
+ assert "package" == package_name.package_name(
+ "apt", "package", "latest", "456",
+ )
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py
new file mode 100644
index 000000000..ae62f1f57
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py
@@ -0,0 +1,158 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ arguments,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestGetSpec:
+ @pytest.mark.parametrize("param", [
+ "auth", "state", "name", "labels", "annotations",
+ ])
+ def test_valid_parameter(self, param):
+ assert set(arguments.get_spec(param).keys()) == set((param,))
+
+ def test_invalid_parameter(self):
+ with pytest.raises(KeyError):
+ arguments.get_spec("bad_parameter_name")
+
+ def test_multiple_parameters(self):
+ assert set(arguments.get_spec("auth", "name", "labels").keys()) == set(
+ ("auth", "name", "labels")
+ )
+
+
+class TestGetSpecPayload:
+ def test_no_key(self):
+ params = dict(
+ name="name",
+ key="value"
+ )
+
+ assert arguments.get_spec_payload(params) == dict()
+
+ def test_spec_payload(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+
+ assert arguments.get_spec_payload(params, "key") == dict(
+ key="value",
+ )
+
+
+class TestGetRenamedSpecPayload:
+ def test_no_mapping(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+ assert arguments.get_renamed_spec_payload(params, dict()) == dict()
+
+ def test_renamed_payload(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+ mapping = dict(
+ name="new_name",
+ )
+ assert arguments.get_renamed_spec_payload(params, mapping) == dict(
+ new_name="name",
+ )
+
+
+class TestGetMutationPayload:
+ def test_name_only(self):
+ params = dict(
+ name="name",
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ ),
+ )
+
+ def test_name_and_namespace(self):
+ params = dict(
+ name="name",
+ namespace="space",
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ namespace="space",
+ ),
+ )
+
+ def test_wanted_key(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+
+ assert arguments.get_mutation_payload(params, "key") == dict(
+ key="value",
+ metadata=dict(
+ name="name",
+ ),
+ )
+
+ def test_namespace_is_none(self):
+ params = dict(
+ name="name",
+ namespace=None,
+ )
+
+ with pytest.raises(AssertionError, match="BUG"):
+ arguments.get_mutation_payload(params)
+
+ def test_labels(self):
+ params = dict(
+ name="name",
+ labels=dict(
+ some="label",
+ numeric=3,
+ ),
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ labels=dict(
+ some="label",
+ numeric="3",
+ ),
+ ),
+ )
+
+ def test_annotations(self):
+ params = dict(
+ name="name",
+ annotations=dict(
+ my="Annotation",
+ number=45,
+ ),
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ annotations=dict(
+ my="Annotation",
+ number="45",
+ ),
+ ),
+ )
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py
new file mode 100644
index 000000000..cf0d8a492
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ bonsai, errors, http,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestGet:
+ def test_url_construction(self, mocker):
+ http_mock = mocker.patch.object(bonsai, "http")
+ http_mock.request.return_value = http.Response(200, "{}")
+
+ bonsai.get("path")
+
+ assert http_mock.request.call_args[0] == (
+ "GET", "https://bonsai.sensu.io/api/v1/assets/path",
+ )
+
+ def test_bad_status(self, mocker):
+ http_mock = mocker.patch.object(bonsai, "http")
+ http_mock.request.return_value = http.Response(400, "{}")
+
+ with pytest.raises(errors.BonsaiError, match="400"):
+ bonsai.get("path")
+
+ def test_invalid_json(self, mocker):
+ http_mock = mocker.patch.object(bonsai, "http")
+ http_mock.request.return_value = http.Response(200, "{ a }")
+
+ with pytest.raises(errors.BonsaiError, match="JSON"):
+ bonsai.get("path")
+
+
+class TestGetAvailableAssetVersions:
+ def test_valid_data(self, mocker):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = dict(
+ versions=[
+ dict(version="1.2.3", assets=[]),
+ dict(version="1.2.4", assets=[]),
+ dict(version="1.2.5", assets=[]),
+ ],
+ )
+
+ result = bonsai.get_available_asset_versions("namespace", "name")
+
+ assert set(("1.2.3", "1.2.4", "1.2.5")) == result
+ assert get.call_args[0] == ("namespace/name",)
+
+ @pytest.mark.parametrize("data", [
+ "invalid",
+ dict(invalid="toplevel"),
+ dict(versions="oh-no"),
+ dict(versions=["not", "ok"]),
+ dict(versions=[dict(invalid="internal")]),
+ ])
+ def test_invalid_data(self, mocker, data):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = data
+
+ with pytest.raises(errors.BonsaiError, match="versions"):
+ bonsai.get_available_asset_versions("namespace", "name")
+
+
+class TestGetAssetVersionBuilds:
+ def test_url_construction(self, mocker):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = dict(spec=dict(builds=[]))
+
+ bonsai.get_asset_version_builds("x", "y", "z")
+
+ assert get.call_args[0] == ("x/y/z/release_asset_builds",)
+
+ @pytest.mark.parametrize("data", [
+ "invalid",
+ dict(missing="spec"),
+ dict(spec="invalid"),
+ dict(spec=dict(missing="builds")),
+ ])
+ def test_invalid_data(self, mocker, data):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = data
+
+ with pytest.raises(errors.BonsaiError, match="spec"):
+ bonsai.get_asset_version_builds("x", "y", "z")
+
+
+class TestGetAssetParameters:
+ def test_valid_all_data(self, mocker):
+ versions = mocker.patch.object(bonsai, "get_available_asset_versions")
+ versions.return_value = set(("t", "u", "v"))
+ builds = mocker.patch.object(bonsai, "get_asset_version_builds")
+ builds.return_value = dict(
+ metadata=dict(
+ annotations=dict(annotation="value"),
+ labels=dict(label="value"),
+ ),
+ spec=dict(builds=[1, 2, 3]),
+ )
+
+ result = bonsai.get_asset_parameters("x/y", "v")
+
+ assert result == dict(
+ labels=dict(label="value"),
+ annotations=dict(annotation="value"),
+ builds=[1, 2, 3],
+ )
+ assert versions.call_args[0] == ("x", "y")
+ assert builds.call_args[0] == ("x", "y", "v")
+
+ def test_valid_minimal_data(self, mocker):
+ versions = mocker.patch.object(bonsai, "get_available_asset_versions")
+ versions.return_value = set(("t", "u", "v"))
+ builds = mocker.patch.object(bonsai, "get_asset_version_builds")
+ builds.return_value = dict(
+ spec=dict(builds=[1, 2, 3]),
+ )
+
+ result = bonsai.get_asset_parameters("x/y", "v")
+
+ assert result == dict(
+ labels=None,
+ annotations=None,
+ builds=[1, 2, 3],
+ )
+ assert versions.call_args[0] == ("x", "y")
+ assert builds.call_args[0] == ("x", "y", "v")
+
+ def test_invalid_name(self, mocker):
+ with pytest.raises(errors.BonsaiError, match="names"):
+ bonsai.get_asset_parameters("x.y", "v")
+
+ def test_invalid_version(self, mocker):
+ versions = mocker.patch.object(bonsai, "get_available_asset_versions")
+ versions.return_value = set(("t", "u"))
+
+ with pytest.raises(errors.BonsaiError, match="Version"):
+ bonsai.get_asset_parameters("x/y", "v")
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py
new file mode 100644
index 000000000..ecc6fc54b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py
@@ -0,0 +1,295 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ client, errors, http
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestAuthHeader:
+ def test_using_valid_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, '{"access_token": "token"}')
+
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ assert dict(Authorization="Bearer token") == c.auth_header
+ assert 1 == request.call_count
+ assert ("GET", "http://example.com/auth") == request.call_args[0]
+ assert "user" == request.call_args[1]["url_username"]
+ assert "pass" == request.call_args[1]["url_password"]
+
+ def test_cache_auth_headers_with_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, '{"access_token": "token"}')
+
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ for i in range(5):
+ c.auth_header
+
+ assert 1 == request.call_count
+
+ def test_login_failure_token_bad_status(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(500, '{"access_token": "token"}')
+
+ with pytest.raises(errors.SensuError, match="500"):
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).auth_header
+
+ def test_login_failure_token_bad_json(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, "{ not a json }")
+
+ with pytest.raises(errors.SensuError, match="JSON"):
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).auth_header
+
+ def test_login_failure_token_missing_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, '{"access_bla": "token"}')
+
+ with pytest.raises(errors.SensuError, match="token"):
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).auth_header
+
+
+class TestVersion:
+ def test_valid_version(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 200, '{"sensu_backend":"5.21.0#sha-here"}',
+ )
+
+ assert c.version == "5.21.0"
+
+ def test_valid_version_is_cached(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ get = mocker.patch.object(c, "get")
+ get.return_value = http.Response(
+ 200, '{"sensu_backend":"5.21.0#sha-here"}',
+ )
+
+ for i in range(4):
+ c.version
+
+ get.assert_called_once()
+
+ def test_non_200_response(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 400, '{"sensu_backend":"5.21.0#sha-here"}',
+ )
+
+ with pytest.raises(errors.SensuError, match="400"):
+ c.version
+
+ def test_bad_json_response(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 200, '"sensu_backend',
+ )
+
+ with pytest.raises(errors.SensuError, match="JSON"):
+ c.version
+
+ def test_missing_backend_version_in_response(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(200, '{}')
+
+ with pytest.raises(errors.SensuError, match="backend"):
+ c.version
+
+ def test_invalid_version(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 200, '{"sensu_backend":"devel"}',
+ )
+
+ assert c.version == c.BAD_VERSION
+
+
+class TestRequest:
+ def test_request_payload_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.side_effect = (
+ http.Response(200, '{"access_token": "token"}'),
+ http.Response(200, "data"),
+ )
+
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).request("PUT", "/path", dict(some="payload"))
+
+ request.assert_called_with(
+ "PUT", "http://example.com/path",
+ payload=dict(some="payload"),
+ headers=dict(Authorization="Bearer token"),
+ validate_certs=True,
+ ca_path=None,
+ )
+
+ def test_request_payload_api_key(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, "data")
+
+ client.Client(
+ "http://example.com/", None, None, "key", False, None,
+ ).request("PUT", "/path", dict(some="payload"))
+
+ request.assert_called_once_with(
+ "PUT", "http://example.com/path",
+ payload=dict(some="payload"),
+ headers=dict(Authorization="Key key"),
+ validate_certs=False,
+ ca_path=None,
+ )
+
+ def test_request_no_payload_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.side_effect = (
+ http.Response(200, '{"access_token": "token"}'),
+ http.Response(200, "data"),
+ )
+
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, "/ca",
+ ).request("PUT", "/path")
+
+ request.assert_called_with(
+ "PUT", "http://example.com/path", payload=None,
+ headers=dict(Authorization="Bearer token"),
+ validate_certs=True,
+ ca_path="/ca",
+ )
+
+ def test_request_no_payload_api_key(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, "data")
+
+ client.Client(
+ "http://example.com/", "u", "p", "key", False, "/ca",
+ ).request("PUT", "/path")
+
+ request.assert_called_once_with(
+ "PUT", "http://example.com/path", payload=None,
+ headers=dict(Authorization="Key key"),
+ validate_certs=False,
+ ca_path="/ca",
+ )
+
+ @pytest.mark.parametrize("status", [401, 403])
+ def test_request_bad_credentials(self, status, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(status, "data")
+
+ with pytest.raises(errors.SensuError, match="credentials"):
+ client.Client(
+ "http://example.com/", None, None, "key", True, None,
+ ).request("PUT", "/path", dict(some="payload"))
+
+ request.assert_called_once_with(
+ "PUT", "http://example.com/path",
+ payload=dict(some="payload"),
+ headers=dict(Authorization="Key key"),
+ validate_certs=True,
+ ca_path=None,
+ )
+
+
+class TestGet:
+ def test_get(self, mocker):
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ c.request = mocker.Mock()
+
+ c.get("/path")
+
+ c.request.assert_called_with("GET", "/path")
+
+
+class TestPut:
+ def test_put(self, mocker):
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ c.request = mocker.Mock()
+
+ c.put("/path", {})
+
+ c.request.assert_called_with("PUT", "/path", {})
+
+
+class TestDelete:
+ def test_delete(self, mocker):
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ c.request = mocker.Mock()
+
+ c.delete("/path")
+
+ c.request.assert_called_with("DELETE", "/path")
+
+
+class TestValidateAuthData:
+ def test_valid_creds(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, None)
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ result = c.validate_auth_data("check_user", "check_pass")
+
+ assert result
+ assert 1 == request.call_count
+ assert ("GET", "http://example.com/auth/test") == request.call_args[0]
+ assert "check_user" == request.call_args[1]["url_username"]
+ assert "check_pass" == request.call_args[1]["url_password"]
+
+ def test_invalid_creds(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(401, None)
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ result = c.validate_auth_data("check_user", "check_pass")
+
+ assert not result
+ assert 1 == request.call_count
+ assert ("GET", "http://example.com/auth/test") == request.call_args[0]
+ assert "check_user" == request.call_args[1]["url_username"]
+ assert "check_pass" == request.call_args[1]["url_password"]
+
+ def test_broken_backend(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(500, None)
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ with pytest.raises(errors.SensuError, match="500"):
+ c.validate_auth_data("check_user", "check_pass")
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py
new file mode 100644
index 000000000..84fddb53a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import ssl
+import sys
+
+import pytest
+
+from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestResponse:
+ def test_with_valid_json(self):
+ resp = http.Response(201, '{"some": ["json", "data", 3]}')
+
+ assert 201 == resp.status
+ assert '{"some": ["json", "data", 3]}' == resp.data
+ assert {"some": ["json", "data", 3]} == resp.json
+
+ def test_with_invalid_json(self):
+ resp = http.Response(404, "")
+
+ assert 404 == resp.status
+ assert "" == resp.data
+ assert resp.json is None
+
+
+class TestRequest:
+ def test_ok_request(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ resp = http.request("GET", "example.com/path")
+
+ assert 200 == resp.status
+ assert "data" == resp.data
+ assert "GET" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+
+ def test_non_20x_status(self, mocker):
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = HTTPError(
+ "url", 404, "missing", {}, None,
+ )
+
+ resp = http.request("GET", "example.com/bad")
+
+ assert 404 == resp.status
+ assert "missing" == resp.data
+ assert "GET" == open_url.call_args[1]["method"]
+ assert "example.com/bad" == open_url.call_args[1]["url"]
+
+ def test_url_error(self, mocker):
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = URLError("Invalid")
+
+ with pytest.raises(errors.HttpError):
+ http.request("GET", "example.com/bad")
+
+ def test_payload_no_headers(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request("PUT", "example.com/path", payload=dict(a=2))
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert '{"a":2}' == open_url.call_args[1]["data"]
+ headers = open_url.call_args[1]["headers"]
+ assert {"content-type": "application/json"} == headers
+
+ def test_payload_with_headers(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request(
+ "PUT", "example.com/path", payload=dict(b=4), headers=dict(h="v"),
+ )
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert '{"b":4}' == open_url.call_args[1]["data"]
+ headers = open_url.call_args[1]["headers"]
+ assert {"content-type": "application/json", "h": "v"} == headers
+
+ def test_payload_overrides_data(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request(
+ "PUT", "example.com/path", payload=dict(a=2), data="data",
+ )
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert '{"a":2}' == open_url.call_args[1]["data"]
+ headers = open_url.call_args[1]["headers"]
+ assert {"content-type": "application/json"} == headers
+
+ def test_data(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request("PUT", "example.com/path", data="data")
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert "data" == open_url.call_args[1]["data"]
+ assert open_url.call_args[1]["headers"] is None
+
+ def test_kwargs(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request("PUT", "example.com/path", a=3, b="f")
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert 3 == open_url.call_args[1]["a"]
+ assert "f" == open_url.call_args[1]["b"]
+
+ def test_cert_error_ssl_module_present(self, mocker):
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = ssl.CertificateError("Invalid")
+
+ with pytest.raises(errors.HttpError):
+ http.request("GET", "example.com/bad")
+
+ def test_cert_error_ssl_module_absent(self, mocker):
+ class Dummy(Exception):
+ pass
+
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = ssl.CertificateError("Invalid")
+ mocker.patch.object(http, "CertificateError", Dummy)
+
+ with pytest.raises(ssl.CertificateError):
+ http.request("GET", "example.com/bad")
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py
new file mode 100644
index 000000000..e4f6ff578
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import role_utils
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoSubjectsDiffer:
+ def test_different_lengths(self):
+ assert role_utils._do_subjects_differ(
+ [{"type": "a", "name": "a"}],
+ [{"type": "a", "name": "a"}, {"type": "a", "name": "b"}]
+ ) is True
+
+ def test_different_type_with_same_name(self):
+ assert role_utils._do_subjects_differ(
+ [{"type": "a", "name": "same"}],
+ [{"type": "b", "name": "same"}]
+ ) is True
+
+ def test_equal_with_different_order_within_type(self):
+ assert role_utils._do_subjects_differ(
+ [{"type": "a", "name": "a2"}, {"type": "a", "name": "a1"}],
+ [{"type": "a", "name": "a1"}, {"type": "a", "name": "a2"}]
+ ) is False
+
+ def test_equal_with_different_order_multiple_types(self):
+ assert role_utils._do_subjects_differ(
+ [
+ {"type": "a", "name": "a2"},
+ {"type": "b", "name": "b1"},
+ {"type": "a", "name": "a1"}
+ ],
+ [
+ {"type": "a", "name": "a1"},
+ {"type": "a", "name": "a2"},
+ {"type": "b", "name": "b1"}
+ ]
+ ) is False
+
+ def test_different(self):
+ assert role_utils._do_subjects_differ(
+ [
+ {"type": "a", "name": "a2"},
+ {"type": "b", "name": "b3"},
+ ],
+ [
+ {"type": "c", "name": "c2"},
+ {"type": "a", "name": "s2"},
+ ]
+ ) is True
+
+
+class TestDoRoleBindingsDiffer:
+ def test_equal_role_binding(self):
+ current = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ {"type": "Group", "name": "g"},
+ ]
+ }
+ desired = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ {"type": "Group", "name": "g"},
+ ]
+ }
+ assert role_utils.do_role_bindings_differ(current, desired) is False
+
+ def test_equal_role_binding_mixed_users_and_groups(self):
+ current = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "Group", "name": "g1"},
+ {"type": "User", "name": "u1"},
+ {"type": "Group", "name": "g2"},
+ {"type": "User", "name": "u2"},
+ ]
+ }
+ desired = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "u2"},
+ {"type": "User", "name": "u1"},
+ {"type": "Group", "name": "g2"},
+ {"type": "Group", "name": "g1"},
+ ]
+ }
+ assert role_utils.do_role_bindings_differ(current, desired) is False
+
+ def test_updated_role_binding_subjects(self):
+ current = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ ]
+ }
+ desired = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ {"type": "Group", "name": "g"},
+ ]
+ }
+ assert role_utils.do_role_bindings_differ(current, desired) is True
+
+
+class TestRuleSets:
+ def test_all_keys_none(self):
+ assert role_utils._rule_set([{}]) == set(
+ ((frozenset(), frozenset(), frozenset()),)
+ )
+
+ def test_rules_multiple(self):
+ assert role_utils._rule_set([{
+ 'verbs': ['list', 'get'],
+ 'resources': ['entities', 'checks'],
+ 'resource_names': None
+ }, {
+ 'verbs': ['list', 'delete'],
+ 'resources': ['entities', 'checks'],
+ 'resource_names': None
+ }]) == set((
+ (frozenset(['delete', 'list']), frozenset(['checks', 'entities']), frozenset()),
+ (frozenset(['get', 'list']), frozenset(['checks', 'entities']), frozenset())
+ ))
+
+ def test_missing_key(self):
+ assert role_utils._rule_set([{
+ 'verbs': ['list', 'get'],
+ 'resources': ['entities', 'checks'],
+ }]) == set(
+ ((frozenset(['get', 'list']), frozenset(['checks', 'entities']), frozenset()),)
+ )
+
+
+class TestDoRulesDiffer:
+ def test_empty_values(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': []}],
+ [{'verbs': []}]
+ ) is False
+
+ def test_rules_when_current_values_are_none(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': None}],
+ [{'verbs': ['get', 'list']}]
+ ) is True
+
+ def test_rules_when_desired_values_are_none(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['get', 'list']}],
+ [{'verbs': None}]
+ ) is True
+
+ def test_rules_are_different(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['list', 'get']}],
+ [{'verbs': ['get', 'delete']}]
+ ) is True
+
+ def test_rules_with_additional_keys_in_current(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['list', 'get'], 'resources': ['checks', 'entities']}],
+ [{'verbs': ['get', 'list']}]
+ ) is True
+
+ def test_rules_are_the_same(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['list', 'get']}],
+ [{'verbs': ['get', 'list']}]
+ ) is False
+
+
+class TestDoRolesDiffer:
+ def test_rules_when_values_in_current_are_none(self):
+ current = {
+ 'rules': [{
+ 'resource_names': None
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_rules_when_values_in_desired_are_none(self):
+ current = {
+ 'rules': [{
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'resource_names': None
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_different_rules_order(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['get', 'list'],
+ 'resources': ['entities', 'checks']
+ }, {
+ 'verbs': ['create', 'delete', 'update'],
+ 'resources': ['assets', 'hooks']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['delete', 'create', 'update'],
+ 'resources': ['hooks', 'assets']
+ }, {
+ 'verbs': ['list', 'get'],
+ 'resources': ['checks', 'entities']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is False
+
+ def test_key_missing_in_current(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['update', 'create'],
+ 'resources': ['hooks', 'assets']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['create', 'update'],
+ 'resources': ['hooks', 'assets'],
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_key_missing_in_desired(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['update', 'create'],
+ 'resources': ['hooks', 'assets'],
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['create', 'update'],
+ 'resources': ['hooks', 'assets']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_role_exists_but_with_additional_rules(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['get', 'list'],
+ 'resources': ['entities', 'check']
+ }, {
+ 'verbs': ['create', 'update', 'delete'],
+ 'resources': ['assets', 'hooks']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['list', 'get'],
+ 'resources': ['check', 'entities']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py
new file mode 100644
index 000000000..f737dce0a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py
@@ -0,0 +1,491 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http, utils,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSync:
+ def test_absent_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, False)
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, True)
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_current_object_present(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, False)
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_called_with("/path")
+
+ def test_absent_current_object_present_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, True)
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_not_called()
+
+ def test_present_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, '{"new": "data"}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with("/path", {"my": "data"})
+
+ def test_present_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(200, '{"current": "data"}'),
+ http.Response(200, '{"new": "data"}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with("/path", {"my": "data"})
+
+ def test_present_current_object_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"current": "data"}')
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"my": "data"}')
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, False,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"my": "data"}')
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, True,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+
+class TestSyncV1:
+ def test_parameter_passthrough(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = (True, {
+ "metadata": {"name": "test", "namespace": "space"},
+ "spec": {"key": "value"},
+ })
+
+ changed, object = utils.sync_v1("absent", "c", "/path", {}, False)
+
+ assert changed is True
+ assert {
+ "metadata": {"name": "test", "namespace": "space"},
+ "key": "value",
+ }
+
+
+class TestDoDiffer:
+ def test_extra_keys_in_current_do_not_matter(self):
+ assert utils.do_differ({"a": "b", "c": 3}, {"a": "b"}) is False
+
+ def test_detect_different_values(self):
+ assert utils.do_differ({"a": "b"}, {"a": "c"}) is True
+
+ def test_detect_missing_keys_in_current(self):
+ assert utils.do_differ({"a": "b"}, {"c": "d"}) is True
+
+ def test_desired_none_values_are_ignored(self):
+ assert utils.do_differ({"a": "b"}, {"c": None}) is False
+
+ def test_metadata_ignores_created_by(self):
+ assert utils.do_differ(
+ dict(metadata=dict(a=1, created_by=2)),
+ dict(metadata=dict(a=1)),
+ ) is False
+
+ def test_metadata_detects_change(self):
+ assert utils.do_differ(
+ dict(metadata=dict(a=1)), dict(metadata=dict(a=2)),
+ ) is True
+
+ def test_metadata_detects_change_in_presence_of_created_by(self):
+ assert utils.do_differ(
+ dict(metadata=dict(a=1, created_by=2)),
+ dict(metadata=dict(a=2)),
+ ) is True
+
+ def test_ignore_keys_do_not_affect_the_outcome(self):
+ assert utils.do_differ(dict(a=1), dict(a=2), "a") is False
+
+ def test_ignore_keys_do_not_mask_other_differences(self):
+ assert utils.do_differ(dict(a=1, b=1), dict(a=2, b=2), "a") is True
+
+
+class TestDoDifferV1:
+ def test_extra_keys_in_current_do_not_matter(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b", "c": 3}}, {"spec": {"a": "b"}},
+ ) is False
+
+ def test_detect_different_values(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b"}}, {"spec": {"a": "c"}},
+ ) is True
+
+ def test_detect_missing_keys_in_current(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b"}}, {"spec": {"c": "d"}},
+ ) is True
+
+ def test_desired_none_values_are_ignored(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b"}}, {"spec": {"c": None}},
+ ) is False
+
+ def test_metadata_ignores_created_by(self):
+ assert utils.do_differ_v1(
+ {"metadata": {"a": 1, "created_by": 2}},
+ {"metadata": {"a": 1}},
+ ) is False
+
+ def test_metadata_detects_change(self):
+ assert utils.do_differ_v1(
+ {"metadata": {"a": 1}}, {"metadata": {"a": 2}},
+ ) is True
+
+ def test_metadata_detects_change_in_presence_of_created_by(self):
+ assert utils.do_differ_v1(
+ {"metadata": {"a": 1, "created_by": 2}},
+ {"metadata": {"a": 2}},
+ ) is True
+
+ def test_ignore_keys_do_not_affect_the_outcome(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": 1}}, {"spec": {"a": 2}}, "a",
+ ) is False
+
+ def test_ignore_keys_do_not_mask_other_differences(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": 1, "b": 1}}, {"spec": {"a": 2, "b": 2}}, "a",
+ ) is True
+
+
+class TestGet:
+ @pytest.mark.parametrize(
+ "status", [100, 201, 202, 203, 204, 400, 401, 403, 500, 501],
+ )
+ def test_abort_on_invalid_status(self, mocker, status):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(status, "")
+
+ with pytest.raises(errors.SyncError, match=str(status)):
+ utils.get(client, "/get")
+ client.get.assert_called_once_with("/get")
+
+ def test_abort_on_invalid_json(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, "")
+
+ with pytest.raises(errors.SyncError, match="JSON"):
+ utils.get(client, "/get")
+ client.get.assert_called_once_with("/get")
+
+ def test_ignore_invalid_json_on_404(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ object = utils.get(client, "/get")
+
+ assert object is None
+ client.get.assert_called_once_with("/get")
+
+ def test_valid_json(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"get": "data"}')
+
+ object = utils.get(client, "/get")
+
+ assert {"get": "data"} == object
+ client.get.assert_called_once_with("/get")
+
+
+class TestDelete:
+ @pytest.mark.parametrize(
+ "status", [100, 200, 201, 202, 203, 400, 401, 403, 500, 501],
+ )
+ def test_abort_on_invalid_status(self, mocker, status):
+ client = mocker.Mock()
+ client.delete.return_value = http.Response(status, "")
+
+ with pytest.raises(errors.SyncError, match=str(status)):
+ utils.delete(client, "/delete")
+ client.delete.assert_called_once_with("/delete")
+
+ def test_valid_delete(self, mocker):
+ client = mocker.Mock()
+ client.delete.return_value = http.Response(204, "{}")
+
+ object = utils.delete(client, "/delete")
+
+ assert object is None
+ client.delete.assert_called_once_with("/delete")
+
+
+class TestPut:
+ @pytest.mark.parametrize(
+ "status", [100, 202, 203, 204, 400, 401, 403, 500, 501],
+ )
+ def test_abort_on_invalid_status(self, mocker, status):
+ client = mocker.Mock()
+ client.put.return_value = http.Response(status, "")
+
+ with pytest.raises(errors.SyncError, match=str(status)):
+ utils.put(client, "/put", {"payload": "data"})
+ client.put.assert_called_once_with("/put", {"payload": "data"})
+
+ @pytest.mark.parametrize("status", [200, 201])
+ def test_valid_put(self, mocker, status):
+ client = mocker.Mock()
+ client.put.return_value = http.Response(status, '{"put": "data"}')
+
+ object = utils.put(client, "/put", {"payload": "data"})
+
+ assert object is None
+ client.put.assert_called_once_with("/put", {"payload": "data"})
+
+
+class TestDictToSingleItemDicts:
+ def test_conversion(self):
+ result = utils.dict_to_single_item_dicts({"a": 0, 1: "b"})
+
+ assert 2 == len(result)
+ for item in ({"a": 0}, {1: "b"}):
+ assert item in result
+
+
+class TestSingleItemDictsToDict:
+ def test_conversion(self):
+ assert dict(a=3, b=4, c=5) == utils.single_item_dicts_to_dict(
+ [dict(a=3), dict(b=4), dict(c=5)]
+ )
+
+
+class TestDictToKeyValueString:
+ def test_conversion(self):
+ result = utils.dict_to_key_value_strings({"a": 0, 1: "b"})
+
+ assert set(("a=0", "1=b")) == set(result)
+
+
+class TestBuildUrlPath:
+ @pytest.mark.parametrize("parts,expectation", [
+ ((), "/"),
+ ((None, None), "/"),
+ ((None, "a", "b", None, None, "c"), "/a/b/c"),
+ (("get/rid of+stuff",), "/get%2Frid%20of%2Bstuff"),
+ (("/", " ", "a"), "/%2F/%20/a"),
+ ])
+ def test_build_url_path_no_namespace(self, parts, expectation):
+ path = "/api/enterprise/store/v1" + expectation
+ assert path == utils.build_url_path(
+ "enterprise/store", "v1", None, *parts
+ )
+
+ @pytest.mark.parametrize("parts,expectation", [
+ ((), "/"),
+ ((None, None), "/"),
+ ((None, "a", "b", None, None, "c"), "/a/b/c"),
+ (("get/rid of+stuff",), "/get%2Frid%20of%2Bstuff"),
+ (("/", " ", "a"), "/%2F/%20/a"),
+ ])
+ def test_build_url_path_with_namespace(self, parts, expectation):
+ path = "/api/core/v2/namespaces/default" + expectation
+ assert path == utils.build_url_path(
+ "core", "v2", "default", *parts
+ )
+
+
+class TestBuildCoreV2Path:
+ def test_build_path_no_namespace(self):
+ assert utils.build_core_v2_path(None, "a").startswith(
+ "/api/core/v2/",
+ )
+
+ def test_build_url_with_namespace(self):
+ assert utils.build_core_v2_path("default", "a").startswith(
+ "/api/core/v2/namespaces/default/",
+ )
+
+
+class TestPrepareResultList:
+ @pytest.mark.parametrize("input,output", [
+ (None, []), # this is mosti likely result of a 404 status
+ ("a", ["a"]),
+ ([], []),
+ ([1, 2, 3], [1, 2, 3]),
+ ([None], [None]), # we leave lists intact, even if they contain None
+ ])
+ def test_list_construction(self, input, output):
+ assert output == utils.prepare_result_list(input)
+
+
+class TestConvertV1ToV2Response:
+ def test_none_passes_through(self):
+ assert utils.convert_v1_to_v2_response(None) is None
+
+ def test_spec_only_if_metadata_is_missing(self):
+ assert utils.convert_v1_to_v2_response(dict(
+ spec=dict(a=1, b=2),
+ )) == dict(a=1, b=2)
+
+ def test_add_metadata_from_toplevel(self):
+ assert utils.convert_v1_to_v2_response(dict(
+ metadata=dict(name="sample"),
+ spec=dict(a=1, b=2),
+ )) == dict(metadata=dict(name="sample"), a=1, b=2)
+
+
+class TestDoSecretsDiffer:
+ @pytest.mark.parametrize("current,desired", [
+ ( # All empty
+ [], [],
+ ),
+ ( # All is equal
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ ),
+ ( # Different order
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ [dict(name="b", secret="2"), dict(name="a", secret="1")],
+ ),
+ ])
+ def test_no_difference(self, current, desired):
+ assert utils.do_secrets_differ(
+ dict(secrets=current), dict(secrets=desired),
+ ) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Different source for variable b
+ [dict(name="b", secret="2")], [dict(name="b", secret="3")],
+ ),
+ ( # Different name
+ [dict(name="a", secret="1")], [dict(name="b", secret="1")],
+ ),
+ ( # Different number of secrets
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ [dict(name="a", secret="1")],
+ ),
+ ])
+ def test_difference(self, current, desired):
+ assert utils.do_secrets_differ(
+ dict(secrets=current), dict(secrets=desired),
+ ) is True
+
+ @pytest.mark.parametrize("secrets,diff", [
+ # Missing secrets and empty list are the same
+ ([], False),
+ # None secrets are treated as empy list of secrets
+ (None, False),
+ # If anything is set, we have difference
+ ([dict(name="n", secret="s")], True),
+ ])
+ def test_missing_secrets(self, secrets, diff):
+ assert utils.do_secrets_differ(dict(), dict(secrets=secrets)) is diff
+ assert utils.do_secrets_differ(dict(secrets=secrets), dict()) is diff
+
+
+class TestDeprecate:
+ def test_ansible_lt_2_9_10(self, mocker):
+ module = mocker.MagicMock()
+ module.deprecate.side_effect = (
+ TypeError("Simulating Ansible 2.9.9 and older"),
+ None, # Success, since no exception is raised
+ )
+
+ utils.deprecate(module, "Test msg", "3.2.1")
+
+ assert module.deprecate.call_count == 2
+ assert module.deprecate.called_once_with("Test msg", version="3.2.1")
+
+ def test_ansible_ge_2_9_10(self, mocker):
+ module = mocker.MagicMock()
+
+ utils.deprecate(module, "Test msg", "3.2.1")
+
+ assert module.deprecate.called_once_with(
+ "Test msg", version="3.2.1", collection_name="sensu.sensu_go",
+ )
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py
new file mode 100644
index 000000000..2287dbc9e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py
@@ -0,0 +1,53 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import json
+
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+
+from mock import patch
+
+
+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:
+ def setup_method(self):
+ self.mock_module = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json,
+ )
+ self.mock_module.start()
+
+ def teardown_method(self):
+ self.mock_module.stop()
+
+
+def generate_name(test_case):
+ return test_case['name']
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py
new file mode 100644
index 000000000..a7204bb5a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py
@@ -0,0 +1,332 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors,
+ utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import ad_auth_provider
+
+from .common.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_no_changes(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+
+ assert ad_auth_provider.do_differ(current, desired) is False
+
+ def test_changes_are_detected(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+ assert ad_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_detected_diff_servers_len(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ dict(
+ host="127.0.0.2",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ ],
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+ assert ad_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_other_params(self):
+ desired = dict(
+ spec=dict(
+ servers=[],
+ groups_prefix="ad",
+ username_prefix="ad",
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+ assert ad_auth_provider.do_differ(current, desired) is True
+
+
+class TestADAutProvider(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="activedirectory",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ad_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/activedirectory"
+ assert payload == dict(
+ type="ad",
+ api_version="authentication/v2",
+ metadata=dict(name="activedirectory"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=None,
+ insecure=False,
+ security="tls",
+ trusted_ca_file=None,
+ client_cert_file=None,
+ client_key_file=None,
+ default_upn_domain=None,
+ include_nested_groups=None,
+ binding=None,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="group",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="sAMAccountName",
+ name_attribute="displayName",
+ object_class="person",
+ ),
+ )
+ ]
+ ),
+ )
+
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="activedirectory",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ default_upn_domain="example.org",
+ include_nested_groups=True,
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="group",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="sAMAccountName",
+ name_attribute="displayName",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ad",
+ username_prefix="ad",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ad_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/activedirectory"
+ assert payload == dict(
+ type="ad",
+ api_version="authentication/v2",
+ metadata=dict(name="activedirectory"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ default_upn_domain="example.org",
+ include_nested_groups=True,
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="group",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="sAMAccountName",
+ name_attribute="displayName",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ad",
+ username_prefix="ad",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ ad_auth_provider.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py
new file mode 100644
index 000000000..8e9298b2c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py
@@ -0,0 +1,251 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import asset
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_equal_assets_with_none_values(self):
+ assert asset.do_differ(
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "sha512": "a",
+ "url": "a",
+ "filters": None
+ },
+ ],
+ },
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "sha512": "a",
+ "url": "a",
+ "headers": None,
+ },
+ ],
+ },
+ ) is False
+
+ def test_equal_assets_with_different_build_content(self):
+ assert asset.do_differ(
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ "headers": {
+ "foo": "bar",
+ "bar": "foo",
+ }
+ },
+ {
+ "url": "http://def.com",
+ "sha512": "def",
+ "filters": ["d == d", "e == e"],
+ },
+
+ ]
+ },
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://def.com",
+ "sha512": "def",
+ "filters": ["e == e", "d == d"],
+ },
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ "headers": {
+ "bar": "foo",
+ "foo": "bar"
+ }
+ },
+ ]
+ },
+ ) is False
+
+ def test_updated_asset(self):
+ assert asset.do_differ(
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ }
+ ],
+ "annotations": {
+ "foo": "bar",
+ }
+ },
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://def.com",
+ "sha512": "abc",
+ },
+ {
+ "url": "http://def.com",
+ "sha512": "abc",
+ "filters": ["abc == def"],
+ }
+ ],
+ },
+ ) is True
+
+ def test_different_assets_with_same_builds(self):
+ assert asset.do_differ(
+ {
+ "name": "a",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ }
+ ],
+ "annotations": {
+ "foo": "bar",
+ }
+ },
+ {
+ "name": "b",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ }
+ ],
+ "annotations": {
+ "bar": "foo"
+ }
+ },
+ ) is True
+
+
+class TestAsset(ModuleTestCase):
+ def test_minimal_asset_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_asset",
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ asset.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/assets/test_asset"
+ assert payload == dict(
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ ),
+ ],
+ metadata=dict(
+ name="test_asset",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_asset_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_asset",
+ namespace="my",
+ state="present",
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ filters=["a", "b", "c"],
+ headers={"header": "h"},
+ ),
+ ],
+ labels={"region": "us-west-1"},
+ annotations={"playbook": 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ asset.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/my/assets/test_asset"
+ assert payload == dict(
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ filters=["a", "b", "c"],
+ headers={"header": "h"},
+ )
+ ],
+ metadata=dict(
+ name="test_asset",
+ namespace="my",
+ labels={"region": "us-west-1"},
+ annotations={"playbook": "12345"},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name="test_asset",
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ )
+ ]
+
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ asset.main()
+
+ def test_failure_empty_builds(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name="test_asset",
+ builds=[],
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ asset.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py
new file mode 100644
index 000000000..aab09b571
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import asset_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestAssetInfo(ModuleTestCase):
+ def test_get_all_assets(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ asset_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/assets"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_asset(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-asset")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ asset_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/assets/sample-asset"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_asset(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-asset")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ asset_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-asset")
+
+ with pytest.raises(AnsibleFailJson):
+ asset_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py
new file mode 100644
index 000000000..bd19bceb1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import auth_provider_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestAuthProviderInfo(ModuleTestCase):
+ def test_get_all_auth_providers(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [dict(spec=dict(a=1)), dict(spec=dict(b=2))]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ auth_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/authentication/v2/authproviders"
+ assert context.value.args[0]["objects"] == [dict(a=1), dict(b=2)]
+
+ def test_get_single_auth_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = dict(spec=dict(a=1))
+ set_module_args(name="sample-auth-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ auth_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/authentication/v2/authproviders/sample-auth-provider"
+ assert context.value.args[0]["objects"] == [dict(a=1)]
+
+ def test_missing_single_auth_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-auth-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ auth_provider_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-auth-provider")
+
+ with pytest.raises(AnsibleFailJson):
+ auth_provider_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py
new file mode 100644
index 000000000..e935a7a9f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py
@@ -0,0 +1,47 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import bonsai, errors
+from ansible_collections.sensu.sensu_go.plugins.modules import bonsai_asset
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestBonsaiAsset(ModuleTestCase):
+ def test_success(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.return_value = dict(sample="value")
+
+ set_module_args(name="name", version="version")
+
+ with pytest.raises(AnsibleExitJson):
+ bonsai_asset.main()
+
+ def test_bonsai_failure(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.side_effect = errors.BonsaiError("Bonsai bad")
+
+ set_module_args(name="name", version="version")
+
+ with pytest.raises(AnsibleFailJson):
+ bonsai_asset.main()
+
+ def test_validation_failure(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.return_value = dict(sample="value")
+
+ set_module_args(version="version")
+
+ with pytest.raises(AnsibleFailJson):
+ bonsai_asset.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py
new file mode 100644
index 000000000..8ea6ecb94
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py
@@ -0,0 +1,327 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import check
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoSetsDiffer:
+ @pytest.mark.parametrize("current,desired,diff", [
+ ([1, 2, 3], [1, 2, 3], False),
+ ([1, 2, 3], [3, 2, 1], False),
+ ([1, 2], [1, 2, 3], True),
+ ([1, 3], [2, 4], True),
+ ])
+ def test_comparison(self, current, desired, diff):
+ c = dict(k=current)
+ d = dict(k=desired)
+
+ assert check.do_sets_differ(c, d, "k") is diff
+
+ def test_missing_keys_are_treated_as_empty_sets(self):
+ current = dict(a=[])
+ desired = dict()
+
+ assert check.do_sets_differ(current, desired, "a") is False
+ assert check.do_sets_differ(desired, current, "a") is False
+
+ def test_nulls_are_treated_as_empty_sets(self):
+ current = dict(a=None)
+ desired = dict(a=[])
+
+ assert check.do_sets_differ(current, desired, "a") is False
+ assert check.do_sets_differ(desired, current, "a") is False
+
+
+class TestDoProxyRequestsDiffer:
+ def test_missing_proxy_requests_in_desired_is_ignored(self):
+ current = dict(proxy_requests=dict(entity_attributes=["a", "b"]))
+ desired = dict()
+
+ assert check.do_proxy_requests_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired,diff", [
+ (["a", "b"], ["a", "b"], False),
+ (["a", "b"], ["b", "a"], False),
+ (None, [], False),
+ (["a", "b"], ["c", "a"], True),
+ (["a", "b"], ["a", "b", "c"], True),
+ ])
+ def test_treat_entity_attributes_as_a_set(self, current, desired, diff):
+ c = dict(proxy_requests=dict(entity_attributes=current))
+ d = dict(proxy_requests=dict(entity_attributes=desired))
+
+ assert check.do_proxy_requests_differ(c, d) is diff
+
+ def test_ignore_missing_entity_attributes_in_desired(self):
+ current = dict(proxy_requests=dict(entity_attributes=["a", "b"]))
+ desired = dict(proxy_requests=dict())
+
+ assert check.do_proxy_requests_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired,diff", [
+ (dict(splay=False), dict(splay=False), False),
+ (dict(splay=False), dict(), False),
+ (dict(splay=False), dict(splay=True), True),
+ (dict(), dict(splay=True), True),
+ ])
+ def test_other_stuff_is_compared_as_usual(self, current, desired, diff):
+ c = dict(proxy_requests=current)
+ d = dict(proxy_requests=desired)
+
+ assert check.do_proxy_requests_differ(c, d) is diff
+
+
+class TestDoCheckHooksDiffer:
+ def test_missing_check_hooks_in_desired_is_ignored(self):
+ current = dict(check_hooks=[dict(warning=["a"])])
+ desired = dict()
+
+ assert check.do_check_hooks_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired,diff", [
+ (["a", "b"], ["a", "b"], False),
+ (["a", "b"], ["b", "a"], False),
+ (["a", "b"], ["c", "a"], True),
+ (["a", "b"], ["a", "b", "c"], True),
+ ])
+ def test_treat_hooks_as_a_set(self, current, desired, diff):
+ c = dict(check_hooks=[dict(warning=current)])
+ d = dict(check_hooks=[dict(warning=desired)])
+
+ assert check.do_check_hooks_differ(c, d) is diff
+
+
+class TestDoDiffer:
+ def test_no_difference(self):
+ assert not check.do_differ(
+ dict(
+ command="sleep",
+ subscriptions=["sub1", "sub2"],
+ handlers=["ha1", "ha2", "ha3"],
+ interval=123,
+ cron="* * * 3 2",
+ publish=False,
+ timeout=30,
+ ttl=60,
+ stdin=True,
+ low_flap_threshold=2,
+ high_flap_threshold=10,
+ runtime_assets=["asset1", "asset2"],
+ check_hooks=[
+ dict(warning=["hook0-1", "hook0-2"]),
+ dict(critical=["hook1-1", "hook1-2"]),
+ ],
+ proxy_entity_name="name",
+ proxy_requests=dict(
+ entity_attributes=["a1", "a2", "a3"],
+ splay=True,
+ splay_coverage=10,
+ ),
+ output_metric_format="influxdb_line",
+ output_metric_handlers=["mhandler1", "mhandler2"],
+ round_robin=False,
+ env_vars=["k1=v1", "k2=v2"],
+ ),
+ dict(
+ command="sleep",
+ subscriptions=["sub2", "sub1"],
+ handlers=["ha3", "ha1", "ha2"],
+ interval=123,
+ cron="* * * 3 2",
+ publish=False,
+ timeout=30,
+ ttl=60,
+ stdin=True,
+ low_flap_threshold=2,
+ high_flap_threshold=10,
+ runtime_assets=["asset2", "asset1"],
+ check_hooks=[
+ dict(critical=["hook1-2", "hook1-1"]),
+ dict(warning=["hook0-2", "hook0-1"]),
+ ],
+ proxy_entity_name="name",
+ proxy_requests=dict(
+ splay=True,
+ entity_attributes=["a3", "a2", "a1"],
+ splay_coverage=10,
+ ),
+ output_metric_format="influxdb_line",
+ output_metric_handlers=["mhandler2", "mhandler1"],
+ round_robin=False,
+ env_vars=["k2=v2", "k1=v1"],
+ )
+ )
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # No diff in params, no secrets
+ dict(name="demo"),
+ dict(name="demo"),
+ ),
+ ( # No diff in params, no diff in secrets
+ dict(name="demo", secrets=[
+ dict(name="n1", secret="s1"), dict(name="n2", secret="s2"),
+ ]),
+ dict(name="demo", secrets=[
+ dict(name="n2", secret="s2"), dict(name="n1", secret="s1"),
+ ]),
+ ),
+ ])
+ def test_no_difference_secrets(self, current, desired):
+ assert check.do_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Diff in params, no diff in secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="a", secret="1")]),
+ ),
+ ( # No diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="demo", secrets=[dict(name="b", secret="2")]),
+ ),
+ ( # Diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="b", secret="2")]),
+ ),
+ ])
+ def test_difference_secrets(self, current, desired):
+ assert check.do_differ(current, desired) is True
+
+
+class TestSensuGoCheck(ModuleTestCase):
+ def test_minimal_check_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_check",
+ command='echo "test"',
+ subscriptions=['switches'],
+ interval=60
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ check.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/checks/test_check"
+ assert payload == dict(
+ command='echo "test"',
+ subscriptions=['switches'],
+ interval=60,
+ metadata=dict(
+ name="test_check",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_check_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_check',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ subscriptions=['checks', 'also_checks'],
+ handlers=['default', 'not_default'],
+ interval=30,
+ publish=True,
+ timeout=30,
+ ttl=100,
+ stdin=False,
+ low_flap_threshold=20,
+ high_flap_threshold=60,
+ proxy_entity_name='switch-dc-01',
+ proxy_requests=dict(
+ entity_attributes=['entity.entity_class == "proxy"'],
+ splay=True,
+ splay_coverage=90
+ ),
+ output_metric_format='nagios_perfdata',
+ output_metric_handlers=['influx-db'],
+ round_robin=True,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness',
+ secrets=[dict(name="a", secret="b")],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ check.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/checks/test_check"
+ assert payload == dict(
+ command='/bin/true',
+ subscriptions=['checks', 'also_checks'],
+ interval=30,
+ timeout=30,
+ publish=True,
+ handlers=['default', 'not_default'],
+ env_vars=['foo=bar'],
+ output_metric_handlers=['influx-db'],
+ ttl=100,
+ output_metric_format='nagios_perfdata',
+ proxy_entity_name='switch-dc-01',
+ proxy_requests=dict(entity_attributes=['entity.entity_class == "proxy"'],
+ splay=True,
+ splay_coverage=90),
+ high_flap_threshold=60,
+ low_flap_threshold=20,
+ round_robin=True,
+ stdin=False,
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name="test_check",
+ namespace="my",
+ ),
+ secrets=[dict(name="a", secret="b")],
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_check',
+ command='/bin/true',
+ subscriptions=['checks', 'also_checks'],
+ handlers=['default', 'not_default'],
+ interval=30,
+ publish=True,
+ timeout=30,
+ ttl=100,
+ stdin=False,
+ low_flap_threshold=20,
+ high_flap_threshold=60,
+ proxy_entity_name='switch-dc-01',
+ proxy_requests=dict(
+ entity_attributes=['entity.entity_class == "proxy"'],
+ splay=True,
+ splay_coverage=90
+ ),
+ output_metric_format='nagios_perfdata',
+ output_metric_handlers=['influx-db'],
+ round_robin=True,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ check.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py
new file mode 100644
index 000000000..53ed5b7c6
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import check_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSensuGoCheckInfo(ModuleTestCase):
+ def test_get_all_checks(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ check_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/checks"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_check(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-check")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ check_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/checks/sample-check"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_check(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-check")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ check_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-check")
+
+ with pytest.raises(AnsibleFailJson):
+ check_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py
new file mode 100644
index 000000000..289933644
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py
@@ -0,0 +1,83 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestCluster(ModuleTestCase):
+ @pytest.mark.parametrize("params", [
+ {"name": "demo", "state": "absent"},
+ {"name": "demo", "api_urls": "url"},
+ ])
+ def test_minimal_parameters(self, mocker, params):
+ mocker.patch.object(utils, "sync_v1").return_value = True, {}
+ set_module_args(**params)
+
+ with pytest.raises(AnsibleExitJson):
+ cluster.main()
+
+ def test_all_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ state="present",
+ name="demo",
+ api_urls=["a", "b"],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/enterprise/federation/v1/clusters/demo"
+ assert payload == dict(
+ type="Cluster",
+ api_version="federation/v1",
+ metadata=dict(name="demo"),
+ spec=dict(api_urls=["a", "b"]),
+ )
+ assert check_mode is False
+
+ @pytest.mark.parametrize("skip", ["api_urls"])
+ def test_missing_required_param_present(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(name="demo", api_urls="url")
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ cluster.main()
+
+ sync_mock.assert_not_called()
+
+ def test_failure(self, mocker):
+ mocker.patch.object(utils, "sync_v1").side_effect = (
+ errors.Error("Bad error")
+ )
+ set_module_args(name="demo", state="absent")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py
new file mode 100644
index 000000000..45158dcd3
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterInfo(ModuleTestCase):
+ def test_all_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k1": "v1"}}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ name="demo",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_info.main()
+
+ def test_get_all_clusters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [
+ {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}},
+ ]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/clusters"
+ assert context.value.args[0]["objects"] == [
+ {"k1": "v1"}, {"k2": "v2"},
+ ]
+
+ def test_get_single_cluster(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k3": "v3"}}
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/clusters/demo"
+ assert context.value.args[0]["objects"] == [{"k3": "v3"}]
+
+ def test_missing_single_cluster(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py
new file mode 100644
index 000000000..48e939a03
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py
@@ -0,0 +1,128 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRole(ModuleTestCase):
+ def test_minimal_cluster_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[]
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterroles/test_cluster_role'
+ assert payload == dict(
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ resource_names=None,
+ )
+ ],
+ metadata=dict(
+ name='test_cluster_role',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_cluster_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role',
+ state='present',
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterroles/test_cluster_role'
+ assert payload == dict(
+ metadata=dict(
+ name='test_cluster_role',
+ ),
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ resource_names=None,
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_cluster_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ )
+ ],
+ )
+ with pytest.raises(AnsibleFailJson):
+ cluster_role.main()
+
+ def test_failure_invalid_verb(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_cluster_role',
+ rules=[
+ dict(
+ verbs=['list', 'invalid'],
+ resources=[],
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py
new file mode 100644
index 000000000..b18c2d226
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py
@@ -0,0 +1,152 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_binding
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRoleBinding(ModuleTestCase):
+ def test_minimal_cluster_role_binding_parameters_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ users=['test_user'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='test_user',
+ type='User',
+ ),
+ ],
+ metadata=dict(
+ name='test_cluster_role_binding',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_cluster_role_binding_parameters_groups(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ groups=['test_group'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='test_group',
+ type='Group',
+ ),
+ ],
+ metadata=dict(
+ name='test_cluster_role_binding',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_cluster_role_binding_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ users=['user_1', 'user_2'],
+ groups=['group_1', 'group_2'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding'
+ assert payload == dict(
+ metadata=dict(
+ name='test_cluster_role_binding',
+ ),
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='group_1',
+ type='Group',
+ ),
+ dict(
+ name='group_2',
+ type='Group',
+ ),
+ dict(
+ name='user_1',
+ type='User',
+ ),
+ dict(
+ name='user_2',
+ type='User',
+ ),
+ ]
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ users=['test_user'],
+ )
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_binding.main()
+
+ def test_failure_missing_groups_or_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_binding.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py
new file mode 100644
index 000000000..34e58451f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_binding_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRoleBindingInfo(ModuleTestCase):
+ def test_get_all_cluster_role_bindings(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterrolebindings"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_cluster_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-cluster-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterrolebindings/test-cluster-role-binding"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_cluster_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-cluster-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_binding_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-cluster-role-binding")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_binding_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py
new file mode 100644
index 000000000..11eadb6a6
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRoleInfo(ModuleTestCase):
+ def test_get_all_cluster_roles(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterroles"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_cluster_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-cluster-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterroles/test-cluster-role"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_cluster_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-cluster-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-cluster-role")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py
new file mode 100644
index 000000000..665b29ab8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py
@@ -0,0 +1,269 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import datastore
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSync(ModuleTestCase):
+ def test_absent_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, False,
+ )
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, True,
+ )
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_current_object_present(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, False,
+ )
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_called_with("/resource")
+
+ def test_absent_current_object_present_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, True,
+ )
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_not_called()
+
+ def test_present_current_object_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(200, '{"spec": {"current": "data"}}'),
+ http.Response(200, '{"spec": {"new": "data"}}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with(
+ "/resource", {"spec": {"my": "data"}},
+ )
+
+ def test_present_current_object_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = (
+ http.Response(200, '{"spec": {"current": "data"}}')
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = (
+ http.Response(200, '{"spec": {"my": "data"}}')
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ False,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = (
+ http.Response(200, '{"spec": {"my": "data"}}')
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ True,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_no_current_object_empty_backend(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, "[]"),
+ http.Response(200, '{"spec": {"new": "data"}}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with(
+ "/resource", {"spec": {"my": "data"}},
+ )
+
+ def test_present_no_current_object_empty_backend_check(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, "[]"),
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ @pytest.mark.parametrize("check", [False, True])
+ def test_present_no_current_object_non_empty_backend(self, mocker, check):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, "[{}]"),
+ )
+
+ with pytest.raises(errors.Error, match="already active"):
+ datastore.sync(
+ "present", client, "/list", "/resource",
+ {"spec": {"my": "data"}}, check,
+ )
+
+ client.put.assert_not_called()
+
+
+class TestDatastore(ModuleTestCase):
+ def test_minimal_datastore_parameters_present(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_datastore",
+ dsn="my-dsn",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ datastore.main()
+
+ state, _client, list_path, resource_path, payload, check_mode = (
+ sync_mock.call_args[0]
+ )
+ assert state == "present"
+ assert resource_path == "/api/enterprise/store/v1/provider/test_datastore"
+ assert list_path == "/api/enterprise/store/v1/provider"
+ assert payload == dict(
+ type="PostgresConfig",
+ api_version="store/v1",
+ metadata=dict(name="test_datastore"),
+ spec=dict(dsn="my-dsn"),
+ )
+ assert check_mode is False
+
+ def test_minimal_datastore_parameters_absent(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_datastore",
+ state="absent",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ datastore.main()
+
+ state, _client, list_path, resource_path, _payload, check_mode = (
+ sync_mock.call_args[0]
+ )
+ assert state == "absent"
+ assert resource_path == "/api/enterprise/store/v1/provider/test_datastore"
+ assert list_path == "/api/enterprise/store/v1/provider"
+ assert check_mode is False
+
+ def test_all_datastore_parameters(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_datastore",
+ dsn="my-dsn",
+ pool_size=543,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ datastore.main()
+
+ state, _client, list_path, resource_path, payload, check_mode = (
+ sync_mock.call_args[0]
+ )
+ assert state == "present"
+ assert resource_path == "/api/enterprise/store/v1/provider/test_datastore"
+ assert list_path == "/api/enterprise/store/v1/provider"
+ assert payload == dict(
+ type="PostgresConfig",
+ api_version="store/v1",
+ metadata=dict(name="test_datastore"),
+ spec=dict(dsn="my-dsn", pool_size=543),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name="test_datastore",
+ dsn="my-dsn",
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ datastore.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py
new file mode 100644
index 000000000..4a1efc9c2
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import datastore_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDatastoreInfo(ModuleTestCase):
+ def test_get_all_datastores(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [dict(spec=1), dict(spec=2)]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ datastore_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/store/v1/provider"
+ assert context.value.args[0]["objects"] == [1, 2]
+
+ def test_get_single_datastore(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = dict(spec=4)
+ set_module_args(name="sample-datastore")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ datastore_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/store/v1/provider/sample-datastore"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_datastore(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-datastore")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ datastore_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-datastore")
+
+ with pytest.raises(AnsibleFailJson):
+ datastore_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py
new file mode 100644
index 000000000..fb098ed87
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py
@@ -0,0 +1,199 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import entity
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ @pytest.mark.parametrize('current', [
+ dict(no=dict(system="here")),
+ dict(system=dict(here="is")),
+ ])
+ def test_no_system_in_desired(self, current):
+ assert entity.do_differ(current, {}) is False
+
+ def test_system_keys_not_in_current_are_ignored(self):
+ assert entity.do_differ(
+ dict(system=dict(a=1, b=2)),
+ dict(system=dict(a=1)),
+ ) is False
+
+ def test_actual_changes_are_detected(self):
+ assert entity.do_differ(
+ dict(system=dict(a=1, b=2)),
+ dict(system=dict(a=2)),
+ ) is True
+
+ def test_missing_keys_are_detected(self):
+ assert entity.do_differ(
+ dict(system=dict(b=2)),
+ dict(system=dict(a=2)),
+ ) is True
+
+ @pytest.mark.parametrize("current,desired", [
+ ([], None), ([], []),
+ (["a"], ["a"]),
+ (["a", "b"], ["b", "a"]),
+ ])
+ def test_no_diff_in_subscriptions(self, current, desired):
+ assert entity.do_differ(
+ dict(subscriptions=current), dict(subscriptions=desired),
+ ) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ([], ["a"]), (["a"], []),
+ (["a"], ["b"]),
+ (["a", "b"], ["a", "c"]),
+ ])
+ def test_diff_in_subscriptions(self, current, desired):
+ print((current, desired))
+ assert entity.do_differ(
+ dict(subscriptions=current), dict(subscriptions=desired),
+ ) is True
+
+
+class TestEntity(ModuleTestCase):
+ def test_minimal_entity_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_entity',
+ entity_class='proxy',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ entity.main()
+
+ state, _c, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/entities/test_entity'
+ assert payload == dict(
+ entity_class='proxy',
+ metadata=dict(
+ name='test_entity',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_entity_parameters_agent_class(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_entity',
+ entity_class='agent',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ entity.main()
+
+ state, _c, path, payload, check_mode, _d = sync_mock.call_args[0]
+ print(payload)
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/entities/test_entity'
+ assert payload == dict(
+ entity_class='agent',
+ metadata=dict(
+ name='test_entity',
+ namespace='default',
+ ),
+ subscriptions=['entity:test_entity'],
+ )
+ assert check_mode is False
+
+ def test_all_entity_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_entity',
+ namespace='my',
+ state='absent',
+ entity_class='proxy',
+ subscriptions=['web', 'prod'],
+ system=dict(
+ hostname='test-entity',
+ os='linux',
+ platform='ubuntu',
+ network=dict(
+ interfaces=[
+ dict(
+ name='lo',
+ addresses=['127.0.0.1/8', '::1/128']
+ ),
+ dict(
+ name='eth0',
+ mac='52:54:00:20:1b:3c',
+ addresses=['93.184.216.34/24']
+ )
+ ])
+ ),
+ last_seen=1522798317,
+ deregister=True,
+ deregistration_handler='email-handler',
+ redact=['password', 'pass', 'api_key'],
+ user='agent'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ entity.main()
+
+ state, _c, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/entities/test_entity'
+ assert payload == dict(
+ entity_class='proxy',
+ subscriptions=['web', 'prod'],
+ system=dict(
+ hostname='test-entity',
+ os='linux',
+ platform='ubuntu',
+ network=dict(
+ interfaces=[
+ dict(
+ name='lo',
+ addresses=['127.0.0.1/8', '::1/128']
+ ),
+ dict(
+ name='eth0',
+ mac='52:54:00:20:1b:3c',
+ addresses=['93.184.216.34/24']
+ )
+ ])
+ ),
+ last_seen=1522798317,
+ deregister=True,
+ deregistration=dict(handler='email-handler'),
+ redact=['password', 'pass', 'api_key'],
+ user='agent',
+ metadata=dict(
+ name='test_entity',
+ namespace='my'
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_entity',
+ entity_class='proxy'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ entity.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py
new file mode 100644
index 000000000..964dcaed0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import entity_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEntityInfo(ModuleTestCase):
+ def test_get_all_entities(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ entity_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/entities"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_entity(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-entity")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ entity_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/entities/sample-entity"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_entity(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-entity")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ entity_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-entity")
+
+ with pytest.raises(AnsibleFailJson):
+ entity_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py
new file mode 100644
index 000000000..982c97c57
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py
@@ -0,0 +1,132 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import etcd_replicator
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEtcdReplicator(ModuleTestCase):
+ @pytest.mark.parametrize("params", [
+ {"name": "demo", "state": "absent"},
+ {"name": "demo", "insecure": True, "url": "url", "resource": "resource"},
+ {
+ "name": "demo",
+ "url": "url",
+ "resource": "resource",
+ "ca_cert": "ca_cert",
+ "cert": "cert",
+ "key": "key",
+ },
+ ])
+ def test_minimal_parameters(self, mocker, params):
+ mocker.patch.object(utils, "sync_v1").return_value = True, {}
+ set_module_args(**params)
+
+ with pytest.raises(AnsibleExitJson):
+ etcd_replicator.main()
+
+ def test_all_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ state="present",
+ name="demo",
+ ca_cert="ca_cert",
+ cert="cert",
+ key="key",
+ insecure=True,
+ url=["a", "b"],
+ api_version="api_version",
+ resource="resource",
+ namespace="namespace",
+ replication_interval=30,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ etcd_replicator.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/enterprise/federation/v1/etcd-replicators/demo"
+ assert payload == dict(
+ type="EtcdReplicator",
+ api_version="federation/v1",
+ metadata=dict(name="demo"),
+ spec=dict(
+ ca_cert="ca_cert",
+ cert="cert",
+ key="key",
+ insecure=True,
+ url="a,b",
+ api_version="api_version",
+ resource="resource",
+ namespace="namespace",
+ replication_interval_seconds=30,
+ ),
+ )
+ assert check_mode is False
+
+ @pytest.mark.parametrize("skip", ["ca_cert", "cert", "key", "url", "resource"])
+ def test_missing_required_param_present_secure(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(
+ name="demo",
+ ca_cert="ca_cert",
+ cert="cert",
+ key="key",
+ url="url",
+ resource="resource",
+ )
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator.main()
+
+ sync_mock.assert_not_called()
+
+ @pytest.mark.parametrize("skip", ["url", "resource"])
+ def test_missing_required_param_present_insecure(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(
+ name="demo",
+ insecure=True,
+ url="url",
+ resource="resource",
+ )
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator.main()
+
+ sync_mock.assert_not_called()
+
+ def test_failure(self, mocker):
+ mocker.patch.object(utils, "sync_v1").side_effect = (
+ errors.Error("Bad error")
+ )
+ set_module_args(name="demo", state="absent")
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py
new file mode 100644
index 000000000..e651dab76
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import etcd_replicator_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEtcdReplicatorInfo(ModuleTestCase):
+ def test_all_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k1": "v1"}}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ name="demo",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ etcd_replicator_info.main()
+
+ def test_get_all_secrets(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [
+ {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}},
+ ]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ etcd_replicator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/etcd-replicators"
+ assert context.value.args[0]["objects"] == [
+ {"k1": "v1"}, {"k2": "v2"},
+ ]
+
+ def test_get_single_replicator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k3": "v3"}}
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ etcd_replicator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/etcd-replicators/demo"
+ assert context.value.args[0]["objects"] == [{"k3": "v3"}]
+
+ def test_missing_single_replicator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ etcd_replicator_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py
new file mode 100644
index 000000000..8808ec2c5
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py
@@ -0,0 +1,286 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import event
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestGetObjects:
+ def test_get_entity(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"entity": "entity"}')
+ resp = event.get_entity(client, 'default', 'entity')
+
+ assert resp == {'entity': 'entity'}
+
+ def test_get_entity_404(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, '')
+
+ with pytest.raises(errors.SyncError,
+ match="Entity with name 'entity' does not exist on remote."):
+ event.get_entity(client, 'default', 'entity')
+
+ def test_get_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"check": "check"}')
+ resp = event.get_check(client, 'default', 'check')
+
+ assert resp == {'check': 'check'}
+
+ def test_get_check_404(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, '')
+
+ with pytest.raises(errors.SyncError,
+ match="Check with name 'check' does not exist on remote."):
+ event.get_check(client, 'default', 'check')
+
+
+class TestEvent(ModuleTestCase):
+ def test_missing_entity_on_remote(self, mocker):
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.side_effect = errors.SyncError('Error')
+
+ set_module_args(
+ entity='awesome_entity',
+ check='awesome_check',
+ )
+
+ with pytest.raises(AnsibleFailJson, match='Error'):
+ event.main()
+
+ def test_missing_check_on_remote(self, mocker):
+ mocker.patch.object(event, 'get_entity')
+ get_check_mock = mocker.patch.object(event, 'get_check')
+ get_check_mock.side_effect = errors.SyncError('Error')
+
+ set_module_args(
+ entity='awesome_entity',
+ check='awesome_check',
+ )
+
+ with pytest.raises(AnsibleFailJson, match='Error'):
+ event.main()
+
+ def test_minimal_event_parameters(self, mocker):
+ send_event_mock = mocker.patch.object(event, 'send_event')
+ send_event_mock.return_value = True, {}
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.return_value = dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ )
+ get_check_mock = mocker.patch.object(event, 'get_check')
+ get_check_mock.return_value = dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ )
+ )
+
+ set_module_args(
+ entity='awesome_entity',
+ check='awesome_check',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ event.main()
+
+ _client, path, payload, check_mode = send_event_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/default/events/awesome_entity/awesome_check'
+ assert payload == dict(
+ metadata=dict(
+ namespace='default'
+ ),
+ entity=dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ ),
+ check=dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ )
+ )
+ )
+ assert check_mode is False
+
+ def test_all_event_parameters(self, mocker):
+ entity_object = dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ )
+ check_object = dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ ),
+ command="check-cpu.sh -w 75 -c 90",
+ handlers=["slack"],
+ interval=60,
+ publish=True,
+ subscriptions=["linux"],
+ )
+ send_event_mock = mocker.patch.object(event, 'send_event')
+ send_event_mock.return_value = True, {}
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.return_value = entity_object
+ get_check_mock = mocker.patch.object(event, 'get_check')
+ get_check_mock.return_value = check_object
+
+ set_module_args(
+ namespace='my',
+ timestamp=1234567,
+ entity='awesome_entity',
+ check='awesome_check',
+ check_attributes=dict(
+ duration=1.945,
+ executed=1522100915,
+ history=[
+ dict(
+ executed=1552505193,
+ status=1
+ ),
+ dict(
+ executed=1552505293,
+ status=0
+ ),
+ dict(
+ executed=1552505393,
+ status=0
+ ),
+ dict(
+ executed=1552505493,
+ status=0
+ )
+ ],
+ issued=1552506033,
+ last_ok=1552506033,
+ output='10',
+ state='passing',
+ status='ok',
+ total_state_change=0
+ ),
+ metric_attributes=dict(
+ handlers=['handler1', 'handler2'],
+ points=[{
+ 'name': 'sensu-go-sandbox.curl_timings.time_total',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.005
+ }, {
+ 'name': 'sensu-go-sandbox.curl_timings.time_namelookup',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.004
+ }]
+ )
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ event.main()
+
+ _client, path, payload, check_mode = send_event_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/my/events/awesome_entity/awesome_check'
+ assert payload == dict(
+ metadata=dict(
+ namespace='my'
+ ),
+ timestamp=1234567,
+ entity=dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ ),
+ check=dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ ),
+ command="check-cpu.sh -w 75 -c 90",
+ handlers=["slack"],
+ interval=60,
+ publish=True,
+ subscriptions=["linux"],
+ duration=1.945,
+ executed=1522100915,
+ history=[
+ dict(
+ executed=1552505193,
+ status=1
+ ),
+ dict(
+ executed=1552505293,
+ status=0
+ ),
+ dict(
+ executed=1552505393,
+ status=0
+ ),
+ dict(
+ executed=1552505493,
+ status=0
+ )
+ ],
+ issued=1552506033,
+ last_ok=1552506033,
+ output='10',
+ state='passing',
+ status=0,
+ total_state_change=0
+ ),
+ metrics=dict(
+ handlers=['handler1', 'handler2'],
+ points=[{
+ 'name': 'sensu-go-sandbox.curl_timings.time_total',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.005
+ }, {
+ 'name': 'sensu-go-sandbox.curl_timings.time_namelookup',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.004
+ }]
+ )
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ entity='awesome_entity',
+ check=dict(
+ name='awesome_check'
+ )
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ event.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py
new file mode 100644
index 000000000..c9270892d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py
@@ -0,0 +1,94 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import event_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEventInfo(ModuleTestCase):
+ def test_get_all_events(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/my/events'
+ assert context.value.args[0]['objects'] == [1, 2, 3]
+
+ def test_get_events_by_entity(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = [1, 2]
+ set_module_args(
+ entity='simple-entity'
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/default/events/simple-entity'
+ assert context.value.args[0]['objects'] == [1, 2]
+
+ def test_get_events_by_check(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = [1, 2]
+ set_module_args(
+ check='simple-check'
+ )
+
+ with pytest.raises(AnsibleFailJson,
+ match=r"missing parameter\(s\) required by 'check': entity"):
+ event_info.main()
+
+ def test_get_single_event_by_entity_and_check(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = 4
+ set_module_args(
+ entity='simple-entity',
+ check='simple-check'
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/default/events/simple-entity/simple-check'
+ assert context.value.args[0]['objects'] == [4]
+
+ def test_no_event_by_entity_and_check(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(
+ entity='simple-entity',
+ check='simple-check'
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.side_effect = errors.Error('Bad error')
+ set_module_args(entity='simple-entity')
+
+ with pytest.raises(AnsibleFailJson):
+ event_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py
new file mode 100644
index 000000000..f92a52623
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py
@@ -0,0 +1,91 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import filter
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestFilter(ModuleTestCase):
+ def test_minimal_filter_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_filter',
+ action='allow',
+ expressions='event.check.occurences == 1',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ filter.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/filters/test_filter'
+ assert payload == dict(
+ action='allow',
+ expressions=['event.check.occurences == 1'],
+ metadata=dict(
+ name='test_filter',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_filter_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_filter',
+ namespace='my',
+ state='absent',
+ action='allow',
+ expressions='event.check.occurences == 1',
+ runtime_assets='awesomeness',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ filter.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/filters/test_filter'
+ assert payload == dict(
+ action='allow',
+ expressions=['event.check.occurences == 1'],
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name='test_filter',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_filter',
+ action='deny',
+ expressions='event.check.occurences == 1',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ filter.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py
new file mode 100644
index 000000000..d8bbcf41a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import filter_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestFilterInfo(ModuleTestCase):
+ def test_get_all_filters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ filter_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/filters"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_filter(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-filter")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ filter_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/filters/sample-filter"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_filter(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-filter")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ filter_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-filter")
+
+ with pytest.raises(AnsibleFailJson):
+ filter_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py
new file mode 100644
index 000000000..bd6aa8059
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py
@@ -0,0 +1,59 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import handler_set
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHandlerSet(ModuleTestCase):
+ def test_all_handler_set_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_handler',
+ namespace='my',
+ state='absent',
+ handlers=['tcp_handler', 'udp_handler']
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ handler_set.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/handlers/test_handler"
+ assert payload == dict(
+ type='set',
+ handlers=['tcp_handler', 'udp_handler'],
+ metadata=dict(
+ name="test_handler",
+ namespace="my",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_handler',
+ state='absent',
+ handlers=['tcp_handler', 'udp_handler']
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ handler_set.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py
new file mode 100644
index 000000000..3b6e6da8d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py
@@ -0,0 +1,93 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import hook
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHook(ModuleTestCase):
+ def test_minimal_hook_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_hook',
+ command='/bin/true',
+ timeout=10,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ hook.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/hooks/test_hook'
+ assert payload == dict(
+ command='/bin/true',
+ timeout=10,
+ metadata=dict(
+ name='test_hook',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_hook_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_hook',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ timeout=30,
+ stdin=True,
+ runtime_assets='awesomeness',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ hook.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/hooks/test_hook'
+ assert payload == dict(
+ command='/bin/true',
+ timeout=30,
+ stdin=True,
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name='test_hook',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_hook',
+ command='/bin/true',
+ timeout=10
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ hook.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py
new file mode 100644
index 000000000..82269840f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import hook_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHookInfo(ModuleTestCase):
+ def test_get_all_hooks(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ hook_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/hooks"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_hook(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-hook")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ hook_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/hooks/sample-hook"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_hook(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-hook")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ hook_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-hook")
+
+ with pytest.raises(AnsibleFailJson):
+ hook_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py
new file mode 100644
index 000000000..4b9e1c463
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py
@@ -0,0 +1,326 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors,
+ utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import ldap_auth_provider
+
+from .common.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_no_changes(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+
+ assert ldap_auth_provider.do_differ(current, desired) is False
+
+ def test_changes_are_detected(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+ assert ldap_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_detected_diff_servers_len(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ dict(
+ host="127.0.0.2",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ ],
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+ assert ldap_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_other_params(self):
+ desired = dict(
+ spec=dict(
+ servers=[],
+ groups_prefix="ldap",
+ username_prefix="ldap",
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+ assert ldap_auth_provider.do_differ(current, desired) is True
+
+
+class TestLDAPAutProvider(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="openldap",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ldap_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/openldap"
+ assert payload == dict(
+ type="ldap",
+ api_version="authentication/v2",
+ metadata=dict(name="openldap"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=None,
+ insecure=False,
+ security="tls",
+ trusted_ca_file=None,
+ client_cert_file=None,
+ client_key_file=None,
+ binding=None,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="groupOfNames",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="uid",
+ name_attribute="cn",
+ object_class="person",
+ ),
+ )
+ ]
+ ),
+ )
+
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="openldap",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="groupOfNames",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="uid",
+ name_attribute="cn",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ldap",
+ username_prefix="ldap",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ldap_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/openldap"
+ assert payload == dict(
+ type="ldap",
+ api_version="authentication/v2",
+ metadata=dict(name="openldap"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="groupOfNames",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="uid",
+ name_attribute="cn",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ldap",
+ username_prefix="ldap",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ ldap_auth_provider.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py
new file mode 100644
index 000000000..9cef32e76
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py
@@ -0,0 +1,126 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import mutator
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ @pytest.mark.parametrize("current,desired", [
+ ( # No diff in params, no secrets
+ dict(name="demo"),
+ dict(name="demo"),
+ ),
+ ( # No diff in params, no diff in secrets
+ dict(name="demo", secrets=[
+ dict(name="n1", secret="s1"), dict(name="n2", secret="s2"),
+ ]),
+ dict(name="demo", secrets=[
+ dict(name="n2", secret="s2"), dict(name="n1", secret="s1"),
+ ]),
+ ),
+ ])
+ def test_no_difference(self, current, desired):
+ assert mutator.do_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Diff in params, no diff in secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="a", secret="1")]),
+ ),
+ ( # No diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="demo", secrets=[dict(name="b", secret="2")]),
+ ),
+ ( # Diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="b", secret="2")]),
+ ),
+ ])
+ def test_difference(self, current, desired):
+ assert mutator.do_differ(current, desired) is True
+
+
+class TestMutator(ModuleTestCase):
+ def test_minimal_mutator_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_mutator',
+ command='/bin/true',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ mutator.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/mutators/test_mutator'
+ assert payload == dict(
+ command='/bin/true',
+ metadata=dict(
+ name='test_mutator',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_mutator_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_mutator',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ timeout=30,
+ runtime_assets='awesomeness',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ secrets=[dict(name="a", secret="b")],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ mutator.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/mutators/test_mutator'
+ assert payload == dict(
+ command='/bin/true',
+ timeout=30,
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name='test_mutator',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ secrets=[dict(name="a", secret="b")],
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_mutator',
+ command='/bion/true'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ mutator.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py
new file mode 100644
index 000000000..b1456c8fb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import mutator_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestMutatorInfo(ModuleTestCase):
+ def test_get_all_mutators(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ mutator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/mutators"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_mutator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-mutator")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ mutator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/mutators/sample-mutator"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_mutator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-mutator")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ mutator_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-mutator")
+
+ with pytest.raises(AnsibleFailJson):
+ mutator_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py
new file mode 100644
index 000000000..e51a63af1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py
@@ -0,0 +1,49 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import namespace
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestNamespace(ModuleTestCase):
+ def test_namespace(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='dev'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ namespace.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/dev'
+ assert payload == dict(
+ name='dev'
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='dev',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ namespace.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py
new file mode 100644
index 000000000..431b6463f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py
@@ -0,0 +1,41 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import namespace_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestNamespaceInfo(ModuleTestCase):
+ def test_get_namespaces(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ namespace_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ namespace_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py
new file mode 100644
index 000000000..0780cdc2f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py
@@ -0,0 +1,118 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors,
+ utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import oidc_auth_provider
+
+from .common.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestADAutProvider(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="oidc_name",
+ additional_scopes=["openid"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ server="https://oidc.example.com:9031",
+ username_claim="email",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ oidc_auth_provider.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[
+ 0
+ ]
+
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/oidc_name"
+ assert payload == dict(
+ type="oidc",
+ api_version="authentication/v2",
+ metadata=dict(name="oidc_name"),
+ spec=dict(
+ additional_scopes=["openid"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ server="https://oidc.example.com:9031",
+ username_claim="email",
+ ),
+ )
+
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="oidc_name",
+ additional_scopes=["groups", "email", "username"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ redirect_uri="http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback",
+ server="https://oidc.example.com:9031",
+ groups_claim="groups",
+ groups_prefix="oidc:",
+ username_claim="email",
+ username_prefix="oidc:",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ oidc_auth_provider.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[
+ 0
+ ]
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/oidc_name"
+ assert payload == dict(
+ type="oidc",
+ api_version="authentication/v2",
+ metadata=dict(name="oidc_name"),
+ spec=dict(
+ additional_scopes=["groups", "email", "username"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ redirect_uri="http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback",
+ server="https://oidc.example.com:9031",
+ groups_claim="groups",
+ groups_prefix="oidc:",
+ username_claim="email",
+ username_prefix="oidc:",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ oidc_auth_provider.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py
new file mode 100644
index 000000000..69c3254a3
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py
@@ -0,0 +1,136 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import pipe_handler
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ @pytest.mark.parametrize("current,desired", [
+ ( # No diff in params, no secrets
+ dict(name="demo"),
+ dict(name="demo"),
+ ),
+ ( # No diff in params, no diff in secrets
+ dict(name="demo", secrets=[
+ dict(name="n1", secret="s1"), dict(name="n2", secret="s2"),
+ ]),
+ dict(name="demo", secrets=[
+ dict(name="n2", secret="s2"), dict(name="n1", secret="s1"),
+ ]),
+ ),
+ ])
+ def test_no_difference(self, current, desired):
+ assert pipe_handler.do_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Diff in params, no diff in secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="a", secret="1")]),
+ ),
+ ( # No diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="demo", secrets=[dict(name="b", secret="2")]),
+ ),
+ ( # Diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="b", secret="2")]),
+ ),
+ ])
+ def test_difference(self, current, desired):
+ assert pipe_handler.do_differ(current, desired) is True
+
+
+class TestPipeHandler(ModuleTestCase):
+ def test_minimal_pipe_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_handler",
+ command='echo "test"'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ pipe_handler.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/handlers/test_handler"
+ assert payload == dict(
+ command='echo "test"',
+ type='pipe',
+ metadata=dict(
+ name="test_handler",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_pipe_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_handler',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness',
+ secrets=[dict(name="a", secret="b")],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ pipe_handler.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/handlers/test_handler"
+ assert payload == dict(
+ command='/bin/true',
+ type='pipe',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ env_vars=['foo=bar'],
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name="test_handler",
+ namespace="my",
+ ),
+ secrets=[dict(name="a", secret="b")],
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_handler',
+ state='absent',
+ command='/bin/true',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ pipe_handler.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py
new file mode 100644
index 000000000..2339e8551
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import handler_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHandlerInfo(ModuleTestCase):
+ def test_get_all_handlers(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ handler_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/handlers"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_handler(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-handler")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ handler_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/handlers/sample-handler"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_handler(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-handler")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ handler_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-handler")
+
+ with pytest.raises(AnsibleFailJson):
+ handler_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py
new file mode 100644
index 000000000..cd1ce4fbf
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py
@@ -0,0 +1,142 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRole(ModuleTestCase):
+ def test_minimal_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[]
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/roles/test_role'
+ assert payload == dict(
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ resource_names=None,
+ )
+ ],
+ metadata=dict(
+ name='test_role',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role',
+ namespace='my',
+ state='present',
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/my/roles/test_role'
+ assert payload == dict(
+ metadata=dict(
+ name='test_role',
+ namespace='my',
+ ),
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ resource_names=None,
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ )
+ ],
+ )
+ with pytest.raises(AnsibleFailJson):
+ role.main()
+
+ def test_failure_invalid_verb(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role',
+ rules=[
+ dict(
+ verbs=['list', 'invalid'],
+ resources=[],
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role.main()
+
+ def test_failure_empty_rules(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role',
+ rules=[]
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py
new file mode 100644
index 000000000..b22eaa7ef
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py
@@ -0,0 +1,214 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role_binding
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRoleBinding(ModuleTestCase):
+ def test_minimal_role_binding_parameters_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ users=['test_user'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_role',
+ type='Role',
+ ),
+ subjects=[
+ dict(
+ name='test_user',
+ type='User',
+ ),
+ ],
+ metadata=dict(
+ name='test_role_binding',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_role_binding_parameters_groups(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ groups=['test_group'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_role',
+ type='Role',
+ ),
+ subjects=[
+ dict(
+ name='test_group',
+ type='Group',
+ ),
+ ],
+ metadata=dict(
+ name='test_role_binding',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_role_binding_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ namespace='my',
+ role='test_role',
+ users=['user_1', 'user_2'],
+ groups=['group_1', 'group_2'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/my/rolebindings/test_role_binding'
+ assert payload == dict(
+ metadata=dict(
+ name='test_role_binding',
+ namespace='my',
+ ),
+ role_ref=dict(
+ name='test_role',
+ type='Role',
+ ),
+ subjects=[
+ dict(
+ name='group_1',
+ type='Group',
+ ),
+ dict(
+ name='group_2',
+ type='Group',
+ ),
+ dict(
+ name='user_1',
+ type='User',
+ ),
+ dict(
+ name='user_2',
+ type='User',
+ ),
+ ]
+ )
+ assert check_mode is False
+
+ def test_role_binding_with_cluster_role(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ cluster_role='test_cluster_role',
+ users=['test_user'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='test_user',
+ type='User',
+ ),
+ ],
+ metadata=dict(
+ name='test_role_binding',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ users=['test_user'],
+ )
+ with pytest.raises(AnsibleFailJson):
+ role_binding.main()
+
+ def test_failure_role_and_cluster_role(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ cluster_role='test_cluster_role',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role_binding.main()
+
+ def test_failure_missing_groups_or_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role_binding.main()
+
+ @pytest.mark.parametrize("params,result", [
+ (
+ dict(role="test-role", cluster_role=None),
+ ("Role", "test-role"),
+ ),
+ (
+ dict(cluster_role="test-cluster-role", role=None),
+ ("ClusterRole", "test-cluster-role"),
+ ),
+ ])
+ def test_infer_role(self, params, result):
+ assert result == role_binding.infer_role(params)
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py
new file mode 100644
index 000000000..2f7725160
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role_binding_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRoleBindingInfo(ModuleTestCase):
+ def test_get_all_role_bindings(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/rolebindings"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/rolebindings/test-role-binding"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_binding_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-role-binding")
+
+ with pytest.raises(AnsibleFailJson):
+ role_binding_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py
new file mode 100644
index 000000000..a2bec291d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRoleInfo(ModuleTestCase):
+ def test_get_all_roles(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/roles"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/roles/test-role"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-role")
+
+ with pytest.raises(AnsibleFailJson):
+ role_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py
new file mode 100644
index 000000000..5912ae738
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secret
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecret(ModuleTestCase):
+ @pytest.mark.parametrize("params", [
+ {"name": "demo", "provider": "env", "id": "MY_VAR"},
+ {"name": "demo", "state": "absent"},
+ ])
+ def test_minimal_parameters(self, mocker, params):
+ mocker.patch.object(utils, "sync_v1").return_value = True, {}
+ set_module_args(**params)
+
+ with pytest.raises(AnsibleExitJson):
+ secret.main()
+
+ def test_all_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ state="present",
+ name="demo",
+ namespace="ns",
+ provider="env",
+ id="MY_ENV_VAR",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secret.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/enterprise/secrets/v1/namespaces/ns/secrets/demo"
+ assert payload == dict(
+ type="Secret",
+ api_version="secrets/v1",
+ metadata=dict(name="demo", namespace="ns"),
+ spec=dict(provider="env", id="MY_ENV_VAR"),
+ )
+ assert check_mode is False
+
+ @pytest.mark.parametrize("skip", ["provider", "id"])
+ def test_missing_required_param_present(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(name="demo", provider="env", id="X")
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ secret.main()
+
+ sync_mock.assert_not_called()
+
+ def test_failure(self, mocker):
+ mocker.patch.object(utils, "sync_v1").side_effect = (
+ errors.Error("Bad error")
+ )
+ set_module_args(name="demo", state="absent")
+
+ with pytest.raises(AnsibleFailJson):
+ secret.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py
new file mode 100644
index 000000000..161204f26
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py
@@ -0,0 +1,86 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secret_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecretInfo(ModuleTestCase):
+ def test_all_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k1": "v1"}}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ name="demo",
+ namespace="ns",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secret_info.main()
+
+ def test_get_all_secrets(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [
+ {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}},
+ ]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secret_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/namespaces/default/secrets"
+ assert context.value.args[0]["objects"] == [
+ {"k1": "v1"}, {"k2": "v2"},
+ ]
+
+ def test_get_single_secret(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k3": "v3"}}
+ set_module_args(name="demo", namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secret_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/namespaces/my/secrets/demo"
+ assert context.value.args[0]["objects"] == [{"k3": "v3"}]
+
+ def test_missing_single_secret(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secret_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleFailJson):
+ secret_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py
new file mode 100644
index 000000000..cce081bf9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py
@@ -0,0 +1,69 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_env
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecretsProviderEnv(ModuleTestCase):
+ def test_no_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_env.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/env'
+ assert payload == dict(
+ type='Env',
+ api_version="secrets/v1",
+ metadata=dict(name='env'),
+ spec={}
+ )
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state='present',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_env.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/env'
+ assert payload == dict(
+ type='Env',
+ api_version="secrets/v1",
+ metadata=dict(name='env'),
+ spec={}
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ secrets_provider_env.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py
new file mode 100644
index 000000000..695152493
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecretsProviderInfo(ModuleTestCase):
+ def test_get_all_secrets_providers(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [dict(spec=1), dict(spec=2)]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secrets_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/providers"
+ assert context.value.args[0]["objects"] == [1, 2]
+
+ def test_get_single_secrets_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = dict(spec=4)
+ set_module_args(name="sample-secrets-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secrets_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/providers/sample-secrets-provider"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_secrets_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-secrets-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secrets_provider_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-secrets-provider")
+
+ with pytest.raises(AnsibleFailJson):
+ secrets_provider_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py
new file mode 100644
index 000000000..fc4c17a87
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py
@@ -0,0 +1,182 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_vault
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_fields_are_ignored(self):
+ desired = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ tls=dict(
+ ca_cert="cert"
+ ),
+ ),
+ ),
+ metadata=dict(
+ name="my-vault"
+ )
+ )
+ current = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ agent_address="", # extra field
+ tls=dict(
+ ca_cert="cert",
+ # extra fields
+ insecure=False,
+ ca_path="path",
+ tls_server_name="server",
+ )
+ ),
+ ),
+ metadata=dict(
+ name="my-vault",
+ created_by="me",
+ )
+ )
+
+ assert secrets_provider_vault.do_differ(current, desired) is False
+
+ def test_changes_are_detected(self):
+ desired = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ tls=dict(
+ ca_cert="cert"
+ ),
+ ),
+ ),
+ metadata=dict(
+ name="my-vault"
+ )
+ )
+ current = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ tls=dict(
+ ca_cert="new-cert",
+ cname="server",
+ ),
+ timeout='15s',
+ ),
+ ),
+ metadata=dict(
+ name="my-vault",
+ )
+ )
+ assert secrets_provider_vault.do_differ(current, desired) is True
+
+
+class TestSecretsProviderVault(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ name='my-vault',
+ state='present',
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_vault.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/my-vault'
+ assert payload == dict(
+ type='VaultProvider',
+ api_version="secrets/v1",
+ metadata=dict(name='my-vault'),
+ spec=dict(
+ client=dict(
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ ),
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ name='my-vault',
+ state='present',
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ tls=dict(
+ ca_cert='/etc/ssl/ca.crt',
+ client_cert='/etc/ssl/client.crt',
+ client_key='/etc/ssl/client.key',
+ cname='my-vault.com',
+ ),
+ timeout=1,
+ max_retries=2,
+ rate_limit=3,
+ burst_limit=4,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_vault.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/my-vault'
+ assert payload == dict(
+ type='VaultProvider',
+ api_version="secrets/v1",
+ metadata=dict(name='my-vault'),
+ spec=dict(
+ client=dict(
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ tls=dict(
+ ca_cert='/etc/ssl/ca.crt',
+ client_cert='/etc/ssl/client.crt',
+ client_key='/etc/ssl/client.key',
+ cname='my-vault.com',
+ ),
+ timeout="1s",
+ max_retries=2,
+ rate_limiter=dict(
+ limit=3,
+ burst=4,
+ ),
+ ),
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ secrets_provider_vault.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py
new file mode 100644
index 000000000..ad0642718
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py
@@ -0,0 +1,120 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import silence
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSilence(ModuleTestCase):
+ def test_minimal_silence_parameters_check(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ check='check'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ silence.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/silenced/%2A%3Acheck' # %2A = *, %3A = :
+ assert payload == dict(
+ check='check',
+ metadata=dict(
+ name='*:check',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_silence_parameters_subscription(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ subscription='subscription'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ silence.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/silenced/subscription%3A%2A' # %3A = :, %2A = *
+ assert payload == dict(
+ subscription='subscription',
+ metadata=dict(
+ name='subscription:*',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_silence_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ namespace='my',
+ subscription='entity:test-entity',
+ check='check',
+ state='absent',
+ begin=1542671205,
+ expire=1542771205,
+ expire_on_resolve=True,
+ reason='because',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ silence.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/silenced/entity%3Atest-entity%3Acheck' # %3A = :
+ assert payload == dict(
+ subscription='entity:test-entity',
+ check='check',
+ begin=1542671205,
+ expire=1542771205,
+ expire_on_resolve=True,
+ reason='because',
+ metadata=dict(
+ name='entity:test-entity:check',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure_when_both_params_are_missing(self):
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson,
+ match='one of the following is required: subscription, check'):
+ silence.main()
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ subscription='subscription'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ silence.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py
new file mode 100644
index 000000000..7a2215411
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py
@@ -0,0 +1,66 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import silence_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSilenceInfo(ModuleTestCase):
+ def test_get_all_silences(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ silence_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/silenced"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_silence(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(subscription="subscription")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ silence_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/silenced/subscription%3A%2A" # %3A = :, %2A = *
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_silence(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(
+ subscription="missing",
+ check="missing",
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ silence_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(check="check")
+
+ with pytest.raises(AnsibleFailJson):
+ silence_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py
new file mode 100644
index 000000000..b4c72f194
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py
@@ -0,0 +1,101 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import socket_handler
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSocketHandler(ModuleTestCase):
+ def test_minimal_socket_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_handler",
+ type='tcp',
+ host='10.0.1.99',
+ port=4444
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ socket_handler.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/handlers/test_handler"
+ assert payload == dict(
+ type='tcp',
+ socket=dict(
+ host='10.0.1.99',
+ port=4444
+ ),
+ metadata=dict(
+ name="test_handler",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_socket_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_handler',
+ namespace='my',
+ state='absent',
+ type='udp',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ host='10.0.1.99',
+ port=4444
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ socket_handler.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/handlers/test_handler"
+ assert payload == dict(
+ type='udp',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ socket=dict(
+ host='10.0.1.99',
+ port=4444
+ ),
+ metadata=dict(
+ name="test_handler",
+ namespace="my",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_handler',
+ state='absent',
+ type='udp',
+ host='10.0.1.99',
+ port=4444
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ socket_handler.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py
new file mode 100644
index 000000000..3eb2796b9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py
@@ -0,0 +1,105 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import tessen
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSync:
+ def test_remote_and_desired_equal(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ changed, object = tessen.sync(client, "/path", {}, False)
+
+ assert changed is False
+ assert object == {}
+
+ def test_remote_and_desired_not_equal(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(200, '{"opt_out": "false"}'),
+ http.Response(200, '{"opt_out": "true"}'),
+ )
+ client.put.return_value = http.Response(200, "")
+ changed, object = tessen.sync(client, "/path", {'opt_out': True}, False)
+
+ assert changed is True
+ assert object == {'opt_out': 'true'}
+ client.put.assert_called_once_with("/path", {'opt_out': True})
+
+ def test_remote_and_desired_equal_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ changed, object = tessen.sync(client, "/path", {}, True)
+
+ assert changed is False
+ assert object == {}
+
+ def test_remote_and_desired_not_equal_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"opt_out": "false"}')
+ changed, object = tessen.sync(client, "/path", {'opt_out': True}, True)
+
+ assert changed is True
+ assert object == {'opt_out': True}
+ client.put.assert_not_called()
+
+
+class TestTessen(ModuleTestCase):
+ def test_enabled(self, mocker):
+ sync_mock = mocker.patch.object(tessen, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ state='enabled'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ tessen.main()
+
+ _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/tessen'
+ assert payload == dict(
+ opt_out=False
+ )
+ assert check_mode is False
+
+ def test_disabled(self, mocker):
+ sync_mock = mocker.patch.object(tessen, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ state='disabled'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ tessen.main()
+
+ _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/tessen'
+ assert payload == dict(
+ opt_out=True
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(tessen, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ state='enabled'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ tessen.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py
new file mode 100644
index 000000000..52e4a7698
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py
@@ -0,0 +1,519 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+from distutils import version
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ arguments, errors, http, utils
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import user
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestUpdatePassword:
+ @pytest.mark.parametrize('check', [False, True])
+ def test_password_is_valid(self, mocker, check):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = True
+
+ changed = user.update_password(client, '/path', 'user', 'pass', check)
+
+ assert changed is False
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_not_called()
+
+ def test_password_is_invalid_older_than_5_21_0(self, mocker):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = False
+ client.version = version.StrictVersion("5.20.2")
+ client.put.return_value = http.Response(201, '')
+
+ changed = user.update_password(client, '/path', 'user', 'pass', False)
+
+ assert changed is True
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_called_once_with('/path/password', dict(
+ username='user', password='pass',
+ ))
+
+ def test_password_is_invalid_5_21_0_or_newer(self, mocker):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = False
+ client.version = version.StrictVersion("5.21.0")
+ client.put.return_value = http.Response(201, '')
+
+ changed = user.update_password(client, '/path', 'user', 'pass', False)
+
+ assert changed is True
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_called_once()
+
+ path, payload = client.put.call_args[0]
+ assert path == '/path/reset_password'
+ assert payload['username'] == 'user'
+
+ # (tadeboro): We cannot validate the value without mocking the bcrypt.
+ # And I would rather see that our code gets tested by actually using
+ # the bcrypt rather than mocking it out. This way, the message
+ # encode/decode stuff gets put through its paces.
+ assert 'password_hash' in payload
+
+ def test_password_is_invalid_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = False
+
+ changed = user.update_password(client, '/path', 'user', 'pass', True)
+
+ assert changed is True
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_not_called()
+
+
+class TestUpdatePasswordHash:
+ @pytest.mark.parametrize('check', [False, True])
+ def test_sensu_go_older_than_5_21_0(self, mocker, check):
+ client = mocker.Mock()
+ client.version = version.StrictVersion("5.20.0")
+
+ with pytest.raises(errors.SensuError):
+ user.update_password_hash(client, '/path', 'user', 'hash', check)
+
+ client.put.assert_not_called()
+
+ def test_sensu_go_newer_than_5_21_0(self, mocker):
+ client = mocker.Mock()
+ client.version = version.StrictVersion("5.21.0")
+ client.put.return_value = http.Response(201, '')
+
+ changed = user.update_password_hash(
+ client, '/path', 'user', 'hash', False,
+ )
+
+ assert changed is True
+ client.put.assert_called_once()
+
+ path, payload = client.put.call_args[0]
+ assert path == '/path/reset_password'
+ assert payload['username'] == 'user'
+ assert payload['password_hash'] == 'hash'
+
+ def test_sensu_go_newer_than_5_21_0_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.version = version.StrictVersion("5.21.0")
+
+ changed = user.update_password_hash(
+ client, '/path', 'user', 'pass', True,
+ )
+
+ assert changed is True
+ client.put.assert_not_called()
+
+
+class TestUpdateGroups:
+ @pytest.mark.parametrize('check', [False, True])
+ def test_update_groups_no_change(self, mocker, check):
+ client = mocker.Mock()
+
+ result = user.update_groups(
+ client, '/path', ['a', 'b'], ['b', 'a'], check,
+ )
+
+ assert result is False
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+ def test_update_groups(self, mocker):
+ client = mocker.Mock()
+ client.put.side_effect = [
+ http.Response(201, ''), http.Response(201, ''),
+ ]
+ client.delete.side_effect = [
+ http.Response(204, ''), http.Response(204, ''),
+ ]
+
+ result = user.update_groups(
+ client, '/path', ['a', 'b', 'c'], ['e', 'd', 'c'], False,
+ )
+
+ assert result is True
+ client.put.assert_has_calls([
+ mocker.call('/path/groups/d', None),
+ mocker.call('/path/groups/e', None),
+ ], any_order=True)
+ client.delete.assert_has_calls([
+ mocker.call('/path/groups/a'),
+ mocker.call('/path/groups/b'),
+ ], any_order=True)
+
+ def test_update_groups_check_mode(self, mocker):
+ client = mocker.Mock()
+
+ result = user.update_groups(
+ client, '/path', ['a', 'b', 'c'], ['e', 'd', 'c'], True,
+ )
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+
+class TestUpdateState:
+ @pytest.mark.parametrize('check', [False, True])
+ @pytest.mark.parametrize('state', [False, True])
+ def test_update_state_no_change(self, mocker, check, state):
+ client = mocker.Mock()
+
+ result = user.update_state(client, '/path', state, state, check)
+
+ assert result is False
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+ def test_disable_user(self, mocker):
+ client = mocker.Mock()
+ client.delete.return_value = http.Response(204, '')
+
+ # Go from disabled == False to disabled == True
+ result = user.update_state(client, '/path', False, True, False)
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_called_once_with('/path')
+
+ def test_disable_user_check_mode(self, mocker):
+ client = mocker.Mock()
+
+ # Go from disabled == False to disabled == True
+ result = user.update_state(client, '/path', False, True, True)
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+ def test_enable_user(self, mocker):
+ client = mocker.Mock()
+ client.put.return_value = http.Response(201, '')
+
+ # Go from disabled == True to disabled == False
+ result = user.update_state(client, '/path', True, False, False)
+
+ assert result is True
+ client.put.assert_called_once_with('/path/reinstate', None)
+ client.delete.assert_not_called()
+
+ def test_enable_user_check_mode(self, mocker):
+ client = mocker.Mock()
+
+ # Go from disabled == True to disabled == False
+ result = user.update_state(client, '/path', True, False, True)
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+
+class TestSync:
+ def test_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ client.put.return_value = http.Response(201, '')
+
+ changed, result = user.sync(
+ None, client, '/path', {'password': 'data'}, False
+ )
+
+ assert changed is True
+ assert {'new': 'data'} == result
+ client.put.assert_called_once_with('/path', {'password': 'data'})
+
+ def test_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+
+ changed, result = user.sync(
+ None, client, '/path', {'password_hash': 'data'}, True
+ )
+
+ assert changed is True
+ assert {} == result
+ client.put.assert_not_called()
+
+ def test_password_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ p_mock.return_value = True
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password='pass'), False
+ )
+
+ assert changed is True
+ assert dict(new='data') == result
+ p_mock.assert_called_once()
+ g_mock.assert_not_called()
+ s_mock.assert_not_called()
+
+ def test_password_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ p_mock.return_value = False
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password='pass'), True
+ )
+
+ assert changed is False
+ assert dict(old='data', username='user') == result
+ p_mock.assert_called_once()
+ g_mock.assert_not_called()
+ s_mock.assert_not_called()
+
+ def test_password_hash_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ mock = mocker.patch.object(user, 'update_password_hash')
+ mock.return_value = True
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password_hash='pass'), False
+ )
+
+ assert changed is True
+ assert dict(new='data') == result
+ mock.assert_called_once()
+
+ def test_password_hash_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ mock = mocker.patch.object(user, 'update_password_hash')
+ mock.return_value = True
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password_hash='pass'), True
+ )
+
+ assert changed is True
+ assert dict(old='data', username='user') == result
+ mock.assert_called_once()
+
+ def test_when_password_is_set_we_ignore_hash(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ p_mock.return_value = True
+ h_mock = mocker.patch.object(user, 'update_password_hash')
+
+ user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password='pass', password_hash='hash'),
+ False
+ )
+
+ p_mock.assert_called_once()
+ h_mock.assert_not_called()
+
+ def test_groups_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ g_mock.return_value = False
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(groups=['a']), client, '/path', dict(groups=['b']), False
+ )
+
+ assert changed is False
+ assert dict(new='data') == result
+ p_mock.assert_not_called()
+ g_mock.assert_called_once()
+ s_mock.assert_not_called()
+
+ def test_groups_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ g_mock.return_value = True
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(x=3, groups=['a']), client, '/path', dict(groups=['b']), True
+ )
+
+ assert changed is True
+ assert dict(x=3, groups=['b']) == result
+ p_mock.assert_not_called()
+ g_mock.assert_called_once()
+ s_mock.assert_not_called()
+
+ def test_state_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+ s_mock.return_value = False
+
+ changed, result = user.sync(
+ dict(disabled=True), client, '/path', dict(disabled=False), False
+ )
+
+ assert changed is False
+ assert dict(new='data') == result
+ p_mock.assert_not_called()
+ g_mock.assert_not_called()
+ s_mock.assert_called_once()
+
+ def test_state_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+ s_mock.return_value = True
+
+ changed, result = user.sync(
+ dict(disabled=True), client, '/path', dict(disabled=False), True
+ )
+
+ assert changed is True
+ assert dict(disabled=False) == result
+ p_mock.assert_not_called()
+ g_mock.assert_not_called()
+ s_mock.assert_called_once()
+
+
+class TestUser(ModuleTestCase):
+ def test_minimal_user_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='alice',
+ password='alice!?pass',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ user.main()
+
+ result, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/users/alice'
+ assert payload == dict(
+ username='alice',
+ password='alice!?pass',
+ disabled=False
+ )
+ assert check_mode is False
+
+ def test_minimal_parameters_on_existing_user(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = dict(username='alice')
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(name='alice')
+
+ with pytest.raises(AnsibleExitJson):
+ user.main()
+
+ result, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/users/alice'
+ assert payload == dict(username='alice', disabled=False)
+ assert check_mode is False
+
+ def test_all_user_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_user',
+ state='disabled',
+ password='password',
+ groups=['dev', 'ops'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ user.main()
+
+ result, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/users/test_user'
+ assert payload == dict(
+ username='test_user',
+ password='password',
+ groups=['dev', 'ops'],
+ disabled=True
+ )
+ assert check_mode is False
+
+ def test_cannot_create_user_without_password(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ set_module_args(
+ name='test_user',
+ state='disabled',
+ groups=['dev', 'ops'],
+ )
+
+ with pytest.raises(AnsibleFailJson, match='without a password'):
+ user.main()
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_user',
+ password='password'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ user.main()
+
+ def test_failure_on_initial_get(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_user',
+ password='password'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ user.main()
+
+ def test_failure_on_missing_bcrypt_5_21_0_or_newer(self, mocker):
+ mocker.patch.object(arguments, 'get_sensu_client').return_value = (
+ mocker.MagicMock(version='5.22.3')
+ )
+ mocker.patch.object(user, 'HAS_BCRYPT', False)
+ set_module_args(
+ name='test_user',
+ password='password'
+ )
+
+ with pytest.raises(AnsibleFailJson, match='bcrypt'):
+ user.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py
new file mode 100644
index 000000000..09fc337f0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import user_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestUserInfo(ModuleTestCase):
+ def test_get_all_users(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ user_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/users"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_user(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-user")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ user_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/users/sample-user"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_user(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-user")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ user_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-user")
+
+ with pytest.raises(AnsibleFailJson):
+ user_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt
new file mode 100644
index 000000000..7f0b6e759
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt
@@ -0,0 +1 @@
+bcrypt