summaryrefslogtreecommitdiffstats
path: root/ansible_collections/sensu
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:03:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 16:03:42 +0000
commit66cec45960ce1d9c794e9399de15c138acb18aed (patch)
tree59cd19d69e9d56b7989b080da7c20ef1a3fe2a5a /ansible_collections/sensu
parentInitial commit. (diff)
downloadansible-upstream.tar.xz
ansible-upstream.zip
Adding upstream version 7.3.0+dfsg.upstream/7.3.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/sensu')
-rw-r--r--ansible_collections/sensu/sensu_go/.circleci/config.yml206
-rw-r--r--ansible_collections/sensu/sensu_go/.flake822
-rw-r--r--ansible_collections/sensu/sensu_go/.gitattributes1
-rw-r--r--ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/bug_report.md38
-rw-r--r--ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/feature_request.md20
-rw-r--r--ansible_collections/sensu/sensu_go/.gitignore9
-rw-r--r--ansible_collections/sensu/sensu_go/CODE_OF_CONDUCT.md207
-rw-r--r--ansible_collections/sensu/sensu_go/COPYING675
-rw-r--r--ansible_collections/sensu/sensu_go/FILES.json3393
-rw-r--r--ansible_collections/sensu/sensu_go/MANIFEST.json33
-rw-r--r--ansible_collections/sensu/sensu_go/Makefile83
-rw-r--r--ansible_collections/sensu/sensu_go/README.md26
-rw-r--r--ansible_collections/sensu/sensu_go/changelogs/changelog.yaml456
-rw-r--r--ansible_collections/sensu/sensu_go/collection.requirements1
-rw-r--r--ansible_collections/sensu/sensu_go/docker/alma-8.docker6
-rw-r--r--ansible_collections/sensu/sensu_go/docker/amazon-1.docker9
-rw-r--r--ansible_collections/sensu/sensu_go/docker/amazon-2.docker9
-rwxr-xr-xansible_collections/sensu/sensu_go/docker/build-all.sh8
-rwxr-xr-xansible_collections/sensu/sensu_go/docker/build.sh13
-rw-r--r--ansible_collections/sensu/sensu_go/docker/centos-6.docker7
-rw-r--r--ansible_collections/sensu/sensu_go/docker/centos-7.docker25
-rw-r--r--ansible_collections/sensu/sensu_go/docker/centos-8.docker6
-rw-r--r--ansible_collections/sensu/sensu_go/docker/debian-10.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/debian-9.docker5
-rw-r--r--ansible_collections/sensu/sensu_go/docker/oracle-8.docker6
-rw-r--r--ansible_collections/sensu/sensu_go/docker/redhat-7.docker7
-rw-r--r--ansible_collections/sensu/sensu_go/docker/rocky-8.docker6
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-5.14.2.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-5.21.3.docker9
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.2.5.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.3.0.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.4.3.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.5.5.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.6.2.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.7.5.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.8.2.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/sensu-6.9.0.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/ubuntu-14.04.docker8
-rw-r--r--ansible_collections/sensu/sensu_go/docker/ubuntu-16.04.docker5
-rw-r--r--ansible_collections/sensu/sensu_go/docker/ubuntu-18.04.docker5
-rw-r--r--ansible_collections/sensu/sensu_go/docs.requirements3
-rw-r--r--ansible_collections/sensu/sensu_go/docs/Makefile20
-rw-r--r--ansible_collections/sensu/sensu_go/docs/Makefile.custom19
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/installation/ansible.cfg10
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/quickstart/inventory.yaml9
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-5.yaml51
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-6.yaml58
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/roles/agent.yaml8
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/roles/backend.yaml7
-rw-r--r--ansible_collections/sensu/sensu_go/docs/examples/roles/install.yaml23
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/conf.py17
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking.rst17
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking/docker-images.rst19
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking/documentation.rst74
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking/releases.rst49
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking/setup.rst94
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking/testing.rst125
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/hacking/windows.rst80
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/index.rst53
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/installation.rst94
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/modules.rst190
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-5.rst42
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-6.rst42
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/release_notes.rst412
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/release_policy.rst50
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/roles.rst11
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/roles/agent.rst53
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/roles/backend.rst192
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/roles/install.rst87
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/sensu_go_5_6_migration.rst74
-rw-r--r--ansible_collections/sensu/sensu_go/docs/source/versioning_sensu_go_installation.rst97
-rw-r--r--ansible_collections/sensu/sensu_go/docs/templates/module.rst.j2108
-rw-r--r--ansible_collections/sensu/sensu_go/integration.requirements6
-rw-r--r--ansible_collections/sensu/sensu_go/meta/execution-environment.yml4
-rw-r--r--ansible_collections/sensu/sensu_go/meta/runtime.yml2
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/action/bonsai_asset.py120
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/annotations.py20
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/auth.py75
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/info.py17
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/labels.py19
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/name.py25
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/namespace.py19
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/requirements.py16
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/secrets.py32
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/doc_fragments/state.py19
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/filter/backends.py28
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/filter/package_name.py40
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/arguments.py120
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/bonsai.py67
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/client.py137
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/debug.py45
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/errors.py27
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/http.py71
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/role_utils.py76
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/module_utils/utils.py176
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/ad_auth_provider.py396
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/asset.py213
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/asset_info.py99
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/auth_provider_info.py145
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/bonsai_asset.py131
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/check.py449
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/check_info.py97
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/cluster.py116
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/cluster_info.py101
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/cluster_role.py164
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding.py145
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding_info.py97
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_info.py98
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/datastore.py170
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/datastore_info.py104
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/entity.py261
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/entity_info.py125
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator.py218
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator_info.py104
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/event.py335
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/event_info.py142
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/filter.py158
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/filter_info.py93
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/handler_info.py94
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/handler_set.py119
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/hook.py149
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/hook_info.py95
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/ldap_auth_provider.py378
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/mutator.py154
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/mutator_info.py96
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/namespace.py87
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/namespace_info.py79
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/oidc_auth_provider.py248
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/pipe_handler.py167
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/role.py165
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/role_binding.py176
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/role_binding_info.py97
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/role_info.py100
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/secret.py131
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/secret_info.py110
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_env.py98
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_info.py111
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_vault.py258
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/silence.py172
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/silence_info.py112
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/socket_handler.py160
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/tessen.py108
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/user.py326
-rw-r--r--ansible_collections/sensu/sensu_go/plugins/modules/user_info.py90
-rw-r--r--ansible_collections/sensu/sensu_go/pytest.ini2
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/README.md5
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/defaults/main.yml5
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/handlers/main.yml35
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/meta/argument_specs.yml58
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/meta/main.yml24
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/tasks/configure.yml8
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/tasks/linux/configure.yml22
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/tasks/main.yml16
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/tasks/start.yml15
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/tasks/windows/configure.yml15
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/templates/agent.yml.j227
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/vars/linux.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/vars/main.yml2
-rw-r--r--ansible_collections/sensu/sensu_go/roles/agent/vars/windows.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/README.md5
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/defaults/main.yml7
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/handlers/main.yml6
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/meta/argument_specs.yml153
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/meta/main.yml24
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/tasks/configure.yml71
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/tasks/main.yml16
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/tasks/start.yml25
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/templates/backend.yml.j2124
-rw-r--r--ansible_collections/sensu/sensu_go/roles/backend/vars/main.yml2
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/README.md5
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/defaults/main.yml12
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/meta/argument_specs.yml69
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/meta/main.yml24
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/apt/install.yml9
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/apt/prepare.yml48
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/install.yml9
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/prepare.yml31
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/main.yml7
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/msi/install.yml35
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/packages.yml8
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/repositories.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/yum/install.yml9
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/tasks/yum/prepare.yml31
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/Alma.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/Amazon.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/CentOS.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/Debian.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/OracleLinux.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/RedHat.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/Rocky.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/Ubuntu.yml3
-rw-r--r--ansible_collections/sensu/sensu_go/roles/install/vars/Windows.yml314
-rw-r--r--ansible_collections/sensu/sensu_go/sanity.requirements6
-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
-rwxr-xr-xansible_collections/sensu/sensu_go/tools/windows-versions.py199
-rw-r--r--ansible_collections/sensu/sensu_go/vagrant/windows/Vagrantfile15
-rw-r--r--ansible_collections/sensu/sensu_go/vagrant/windows/back.yaml18
-rw-r--r--ansible_collections/sensu/sensu_go/vagrant/windows/inventory.yaml10
-rw-r--r--ansible_collections/sensu/sensu_go/vagrant/windows/play.yaml14
379 files changed, 33834 insertions, 0 deletions
diff --git a/ansible_collections/sensu/sensu_go/.circleci/config.yml b/ansible_collections/sensu/sensu_go/.circleci/config.yml
new file mode 100644
index 00000000..96612ff4
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/.circleci/config.yml
@@ -0,0 +1,206 @@
+version: "2.1"
+
+workflows:
+ version: 2
+ main_workflow:
+ jobs:
+ - sanity_test:
+ matrix: &matrix
+ parameters:
+ # ansible is branch name in ansible/ansible git repo
+ ansible:
+ - stable-2.9
+ - stable-2.10
+ - stable-2.11
+ - stable-2.12
+ - stable-2.13
+ - stable-2.14
+
+
+ - unit_test:
+ matrix: *matrix
+
+ - integration_test_git:
+ requires:
+ - sanity_test
+ - unit_test
+ matrix: *matrix
+
+ - integration_test_galaxy:
+ filters: { branches: { only: [ stable ] } }
+ requires:
+ - integration_test_git
+ matrix: *matrix
+
+ cron_master:
+ triggers:
+ - schedule:
+ cron: "12 5 * * 0,3"
+ filters: { branches: { only: [ master ] } }
+ jobs:
+ - integration_test_git:
+ matrix: *matrix
+
+ cron_released:
+ triggers:
+ - schedule:
+ cron: "12 5 * * 1,4"
+ filters: { branches: { only: [ stable ] } }
+ jobs:
+ - integration_test_galaxy:
+ matrix: *matrix
+
+ cron_ansible_devel:
+ triggers:
+ - schedule:
+ cron: "12 5 * * 2,5"
+ filters: { branches: { only: [ master ] } }
+ jobs:
+ - sanity_test:
+ matrix: &devel-matrix
+ parameters:
+ ansible: [ devel ]
+
+ - unit_test:
+ matrix: *devel-matrix
+
+ - integration_test_git:
+ requires:
+ - sanity_test
+ - unit_test
+ matrix: *devel-matrix
+
+ windows_version_check:
+ triggers:
+ - schedule:
+ cron: "12 3 * * 0,2,4"
+ filters: { branches: { only: [ master ] } }
+ jobs:
+ - windows_version_check
+
+jobs:
+ sanity_test:
+ parameters:
+ ansible:
+ description: Ansible version to use
+ type: string
+ machine: &ci-machine
+ image: ubuntu-2004:202101-01
+ working_directory: ~/ansible_collections/sensu/sensu_go
+ steps:
+ - wrapper:
+ ansible: << parameters.ansible >>
+ kind: sanity
+ test_commands:
+ - run: make sanity
+
+ unit_test:
+ parameters:
+ ansible:
+ description: Ansible version to use
+ type: string
+ machine: *ci-machine
+ working_directory: ~/ansible_collections/sensu/sensu_go
+ steps:
+ - wrapper:
+ ansible: << parameters.ansible >>
+ kind: sanity
+ test_commands:
+ - run: make units
+ - store_artifacts:
+ path: tests/output/reports/coverage
+ destination: coverage-report
+ - store_test_results:
+ path: tests/output/junit
+
+ integration_test_git:
+ parallelism: 6
+ parameters:
+ ansible:
+ description: Ansible version to use
+ type: string
+ machine: *ci-machine
+ working_directory: ~/sensu_go
+ steps:
+ - wrapper:
+ ansible: << parameters.ansible >>
+ kind: integration
+ test_commands:
+ - run: ansible-galaxy collection build
+ - run: ansible-galaxy collection install sensu-sensu_go-*.tar.gz
+ - run_integration_tests
+
+ integration_test_galaxy:
+ parallelism: 6
+ parameters:
+ ansible:
+ description: Ansible version to use
+ type: string
+ machine: *ci-machine
+ working_directory: ~/sensu_go
+ steps:
+ - wrapper:
+ ansible: << parameters.ansible >>
+ kind: integration
+ test_commands:
+ - run: |
+ ansible-galaxy collection install \
+ sensu.sensu_go:$(grep version: galaxy.yml | cut -d" " -f2)
+ - run_integration_tests
+
+ windows_version_check:
+ docker:
+ - image: cimg/python:3.10.6
+ steps:
+ - checkout
+ - run: pip3 install pyyaml
+ - run: make check_windows_versions
+
+commands:
+ run_integration_tests:
+ description: Run integration tests
+ steps:
+ - run: ansible-galaxy collection install community.docker
+ - run:
+ name: Display scheduled scenarios
+ command: |
+ circleci tests glob "tests/integration/molecule/*/molecule.yml" \
+ | circleci tests split --split-by=timings
+ - run: make integration_ci
+ - store_test_results:
+ path: test_results
+ - store_artifacts:
+ path: test_results
+
+ wrapper:
+ description: Wrapper command that takes care of venv caching
+ parameters:
+ ansible:
+ description: Ansible version to install
+ type: string
+ kind:
+ description: Test kind (used to construct cache name)
+ type: string
+ test_commands:
+ description: Test commands to execute
+ type: steps
+ steps:
+ - checkout: { path: . }
+ - run:
+ name: Generate cache id file
+ command: |
+ rm -f cache-id.txt
+ echo "week $(date +%V)" >> cache-id.txt
+ echo "ansible << parameters.ansible >>" >> cache-id.txt
+ echo "kind << parameters.kind >>" >> cache-id.txt
+ echo "cache busting string 2" >> cache-id.txt
+ - restore_cache:
+ key: '{{ checksum "cache-id.txt" }}'
+ - run:
+ name: Install Ansible
+ command: pip3 install -U https://github.com/ansible/ansible/archive/<< parameters.ansible >>.tar.gz --disable-pip-version-check
+ - steps: << parameters.test_commands >>
+ - save_cache:
+ key: '{{ checksum "cache-id.txt" }}'
+ paths:
+ - "~/venv"
diff --git a/ansible_collections/sensu/sensu_go/.flake8 b/ansible_collections/sensu/sensu_go/.flake8
new file mode 100644
index 00000000..c54d5453
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/.flake8
@@ -0,0 +1,22 @@
+# This configuration mirrors the configuration that is used when linting
+# collections on import into Ansible Galaxy.
+
+# TODO(@tadeboro): Some of our lines are almost twice the optimal reading
+# length of 60-80 chars. It would be great to get line lengths down to 79, but
+# this is not feasible at the moment since we have cca. 300 lines that are
+# longer. What we need is a way of preventing new long lines of getting into
+# the codebase, which means running flake8 with stricter rules on changed
+# lines only.
+
+[flake8]
+max-line-length = 144
+max-doc-length = 92
+
+exclude =
+ # ansible-test creates all sorts of temporary stuff that we do not care
+ # about.
+ tests/output
+
+per-file-ignores =
+ # Modules have their imports listed after the metadata.
+ plugins/modules/*.py:E402
diff --git a/ansible_collections/sensu/sensu_go/.gitattributes b/ansible_collections/sensu/sensu_go/.gitattributes
new file mode 100644
index 00000000..a19ade07
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/.gitattributes
@@ -0,0 +1 @@
+CHANGELOG.md merge=union
diff --git a/ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/bug_report.md b/ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..5fb34016
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Bug description**
+A clear and concise description of what the issue is.
+
+**How to reproduce the bug**
+1. Run the command `ansible-playbook -i inv ...`.
+
+**If applicable, content of the files used in commands from the previous step**
+For example, `inv` file from previous sample could contain:
+```
+[mygroup]
+10.5.126.31
+```
+
+**Expected result**
+A clear and concise description of what you expected to happen.
+
+**Actual result**
+A clear and concise description of what actually happened. Make sure you
+include any error messages, since they usually offer vital clues to the
+bug-fixing process.
+
+
+**Component versions**
+ - Ansible Version [e.g. 2.9.0]
+ - Sensu Go Collection Version [e.g. 0.7.5]
+ - Sensu Go Backend Version [e.g. 5.14.1]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/feature_request.md b/ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..4fe86d5e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: feature
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/ansible_collections/sensu/sensu_go/.gitignore b/ansible_collections/sensu/sensu_go/.gitignore
new file mode 100644
index 00000000..8e935789
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/.gitignore
@@ -0,0 +1,9 @@
+tests/output
+tests/runner
+docs/build
+docs/source/modules
+test_results
+.pytest_cache
+*.pyc
+*.swp
+.vagrant
diff --git a/ansible_collections/sensu/sensu_go/CODE_OF_CONDUCT.md b/ansible_collections/sensu/sensu_go/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..1c1f75cb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/CODE_OF_CONDUCT.md
@@ -0,0 +1,207 @@
+# Overview
+
+As a member of the Sensu community, we ask that you keep the following in mind:
+
+ * Be kind and welcoming to others.
+ * Respect personal space and ask before you enter it. Remember not
+ everyone likes to be touched or hugged or joked with.
+ * Be mindful of language. Do not disparage others. Listen more than you talk.
+ * Remember that harassment and sexist, racist comments or behavior are not
+ appropriate in our Community.
+
+Sensu in-person and online events are intended for networking and
+collaboration in the monitoring community. Attendees violating these rules may
+be asked to leave any event we host without a refund at the sole discretion of
+the event organizers.
+
+The same rules apply when participating in other Sensu-related forums and
+mailing lists, contributing to code and documentation, and in private
+correspondence in Sensu-related space.
+
+Thank you for helping make this a welcoming place for all.
+
+
+## Important numbers
+
+Conference staff is happy to help participants contact venue security or local
+law enforcement, provide escorts, or otherwise assist those experiencing
+harassment to feel safe for the duration of the event. We value your
+attendance.
+
+If your physical safety or the physical safety of others is at risk, please
+dial or text emergency number (911 in Americas, 112 in Europe, see [wiki][]
+page for more numbers). For other needs, contact the relevant authorities
+below:
+
+ [wiki]:
+ https://en.wikipedia.org/wiki/List_of_emergency_telephone_numbers
+ (List of emergency numbers)
+
+**Code of Conduct team**
+The Sensu Community team
+community@sensu.io
+[+1 855-997-3678 ext 510](tel:+18559973678,,510)
+
+
+# Full Code of Conduct
+
+In order to foster an inclusive, kind, harassment-free, and cooperative
+community, Sensu enforces this code of conduct on our software projects and
+events. To make clear what is expected, all delegates/attendees, speakers,
+exhibitors, organizers, and volunteers at any Sensu activity are required to
+read and follow this Code of Conduct. Same rules apply to online activities
+such as opening pull requests, submitting issues, and posting content on Sensu
+forums.
+
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we, as
+contributors and maintainers, pledge to making participation in our project
+and our community a harassment-free experience for everyone, regardless of
+age, body size, disability, ethnicity, gender identity and expression, level
+of experience, nationality, personal appearance, race, religion, or sexual
+identity and orientation.
+
+
+## Enforcement
+
+Participants asked to stop any harassing behavior are expected to comply
+immediately.
+
+At events, exhibitors in the expo hall, sponsor or vendor booths, or similar
+activities are also subject to the anti-harassment policy. In particular,
+exhibitors should never use sexualized images, activities, or other material.
+Booth staff (including volunteers) should not use sexualized
+clothing/uniforms/costumes, or otherwise create a sexualized environment.
+
+If a participant engages in behavior that violates this code of conduct, the
+event organizers, project maintainers, or forum administrators will take any
+action they deem appropriate, including warning the offender, expulsion from
+the event with no refund, or blocking access to repository.
+
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+ * Using welcoming and inclusive language
+ * Being respectful of differing viewpoints and experiences
+ * Gracefully accepting constructive criticism
+ * Focusing on what is best for the community
+ * Showing empathy towards other community members
+
+Examples of behavior that contributes to creating a negative environment
+include:
+
+ * The use of sexualized language or imagery and unwelcome sexual attention
+ or advances
+ * Trolling, insulting/derogatory comments, and personal or political attacks
+ * Public or private harassment
+ * Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+
+Other conduct which could reasonably be considered inappropriate in a
+professional setting All communication should be appropriate for a
+professional audience, including people of many different backgrounds. Sexual
+language and imagery is not appropriate at any time in our community,
+including presentations.
+
+
+## Our Responsibilities
+
+Project contributors (including maintainers) are responsible for clarifying
+the standards of acceptable behavior and are expected to take appropriate and
+fair corrective action in response to any instances of unacceptable behavior.
+
+Project contributors have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an
+appointed representative at an online or offline event. Representation of a
+project may be further defined and clarified by project maintainers.
+
+
+## Recommendations
+
+The team leading the Sensu Community and enforcing its Code of Conduct
+recognize that most participants mean well, but may not be familiar with
+behaviors that can harm others unintentionally. The following includes
+recommendations to be effective and thoughtful participants.
+
+
+### Be kind to others
+
+Kindness is about providing people what they need. It's best to start by
+listening to others more than you speak. From there, it is all about providing
+people with a sense that they are welcome. Here are some ideas of how to do
+that: invite someone to your lunch table; smile at an attendee; introduce
+yourself to someone new; and sit with folks you've never met; answer forum
+questions politely and link to relevant documentation; help first time
+contributors follow developer guides; say thank you when code contribution is
+accepted. Encourage others to share.
+
+
+### Respect personal space
+
+Respect others' boundaries. Respect personal space and ask before you enter
+it. Remember not everyone likes to be touched or hugged. If you are a hugger,
+it is respectful to directly ask ("I'm a hugger. Would you like a hug?").
+
+
+### Beware of gendered double standards
+
+Be conscious of your behavior reinforcing one set of introductions for women
+and a different set for men. If you shake people's hands when you meet them,
+offer to do so consistently regardless of gender. If you hug people when you
+meet them, offer to do so consistently regardless of gender.
+
+
+### Be respectful
+
+Not all of us will agree all the time, but disagreement is no excuse for poor
+behavior and poor manners. This is doubly important in online communication
+where it is easy to forget that we are interaction with another human being.
+We do not tolerate harassment of event participants in any form.
+
+Harassment includes: offensive verbal comments related to gender, gender
+identity, sexual orientation, disability, physical appearance, body size,
+race, religion; sexual images in public spaces; deliberate intimidation,
+stalking, or following; harassing photography or recording; sustained
+disruption of talks or other events; unwanted physical contact; and unwelcome
+sexual attention.
+
+
+### Choose your words
+
+Be thoughtful with the words you choose. Remember that sexist, racist, and
+other exclusionary jokes can be offensive to those around you. Excessive
+swearing can be off-putting to those around you. When you want to bring up
+topics that may relate to past trauma, mention the appropriate trigger/content
+warnings (if necessary, read more about trigger warnings).
+
+
+### Photography
+
+It's always nice to ask before taking a photo. If someone does not want to be
+photographed, video or audio recorded, please respect their wishes.
+
+
+## License
+
+This Code of Conduct began as an adaptation of Contributor Covenant, version
+1.4, available at
+https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. It was
+influenced by Geek Feminism wiki, created by the Ada Initiative under a
+Creative Commons Zero license. We also used language about events from the
+Conference Code of Conduct by 2017.djangocon.us/coc/.
diff --git a/ansible_collections/sensu/sensu_go/COPYING b/ansible_collections/sensu/sensu_go/COPYING
new file mode 100644
index 00000000..10926e87
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/COPYING
@@ -0,0 +1,675 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/ansible_collections/sensu/sensu_go/FILES.json b/ansible_collections/sensu/sensu_go/FILES.json
new file mode 100644
index 00000000..953d4bec
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/FILES.json
@@ -0,0 +1,3393 @@
+{
+ "files": [
+ {
+ "name": ".",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".circleci",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".circleci/config.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5374567ce315af2036683dfb61c105ae52e9242145eeef5a8e7fce24f5866c93",
+ "format": 1
+ },
+ {
+ "name": ".flake8",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1d14da49e3dc70a10ecb9132d3bc0c1a54e5dac2f28b9e712924e6b33033cfb4",
+ "format": 1
+ },
+ {
+ "name": ".gitattributes",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eda83702f1ee1fc2e4f7252ac946830797b83eb36aeb4ccff6133ab2b60c3148",
+ "format": 1
+ },
+ {
+ "name": ".github",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".github/ISSUE_TEMPLATE",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".github/ISSUE_TEMPLATE/bug_report.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1cbd84cd477255ed9c742dfff3b3e6d224c105e1a291aa5942f1670597afb011",
+ "format": 1
+ },
+ {
+ "name": ".github/ISSUE_TEMPLATE/feature_request.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "aa20f06cc398fde7b9ce6be01f4d39f7c5b6e8efbaf40b229983f994d5f5e49e",
+ "format": 1
+ },
+ {
+ "name": ".gitignore",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f7a82b393b6c5cb3f95294240659749465a5b004323671a41d367f3471b617ae",
+ "format": 1
+ },
+ {
+ "name": "CODE_OF_CONDUCT.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5625beeb80d7cca3f26f389bd847dc23acd6a29877d2f35cb5c33037721835e7",
+ "format": 1
+ },
+ {
+ "name": "COPYING",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0ae0485a5bd37a63e63603596417e4eb0e653334fa6c7f932ca3a0e85d4af227",
+ "format": 1
+ },
+ {
+ "name": "Makefile",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "aee376b38b859d8d33a6e63e1bc99297d0f6e2772a62381ba961143ec9718cb7",
+ "format": 1
+ },
+ {
+ "name": "README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "25185d310c91a95e9ef28fbf2a9936fcb00b6f4f1851e1ee4cec84b5bc8e43fd",
+ "format": 1
+ },
+ {
+ "name": "changelogs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "changelogs/changelog.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "49a5c005a5bf7c0cbf04e11acb92c00291d73d78ad419b6613082ff5658afad2",
+ "format": 1
+ },
+ {
+ "name": "collection.requirements",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "06447fa81cc36d2bb270ccc7584d6fcd7adf61e700cd456642ce86d82ba4a8d2",
+ "format": 1
+ },
+ {
+ "name": "docker",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docker/alma-8.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "77faf407d99d19572bee379185ecc6e1db6ca09b24a8931c5ba2187efdfe6c7b",
+ "format": 1
+ },
+ {
+ "name": "docker/amazon-1.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e6423e2dc42361bdd99cf66638ef484b52052fb90c4e06425079f42a36a8f4e0",
+ "format": 1
+ },
+ {
+ "name": "docker/amazon-2.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0539425ff1396178a71e179b79281693c0a76e55f9b77baaef5313f75ed9a5b4",
+ "format": 1
+ },
+ {
+ "name": "docker/build-all.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d07218d70fff8b732f66a1fba749dbdcdd44e6cacb5f82bb3c7df4145cbd3266",
+ "format": 1
+ },
+ {
+ "name": "docker/build.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f5e11180ff7a8271e6e2312bffeb8d0f9530fde88056cc7101e14dff3e7ab770",
+ "format": 1
+ },
+ {
+ "name": "docker/centos-6.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f17b5627098349ba0b837f0325bae74cf1a2026c889fa1a2b8451bf278cfc110",
+ "format": 1
+ },
+ {
+ "name": "docker/centos-7.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "68c31a313c8d77cce02ca852fecba959afce224d76fa3e146a945c79b62eac0f",
+ "format": 1
+ },
+ {
+ "name": "docker/centos-8.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0b90bd28a61d8ac05908c717c28793626cee7071d30a7a29425f81d0afcb614e",
+ "format": 1
+ },
+ {
+ "name": "docker/debian-10.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a6189f24a19571821e78a20a356e77f8d2f3741b028e9c16aa449425e3088f05",
+ "format": 1
+ },
+ {
+ "name": "docker/debian-9.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f80ce3d252420117d1621e76c50896cc8dd1a50ca7eb05c9690cc414ce19c6fc",
+ "format": 1
+ },
+ {
+ "name": "docker/oracle-8.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0e2fe59fd5ecb49f0c28ce2d9549f2864713797abd8240a64f4477991bbef19e",
+ "format": 1
+ },
+ {
+ "name": "docker/redhat-7.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4f9601854dbad9e17e81ab915e09c8fa9c2dc2c1d255c7d9bfa47b387b6d09af",
+ "format": 1
+ },
+ {
+ "name": "docker/rocky-8.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4aedcd2b720be1d1af27970a083a3109764c4aca9a831cbbfc3b1550937bdb1a",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-5.14.2.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fb1fdf845b77606d01c4ac3b39fb57158bf8a36fb0be68c39e225923b7f10ff5",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-5.21.3.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e9ccfbd7ed743d543502484133da23bc7be30a0fc7cce67a7c5b16717b30a28",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.2.5.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7b422c63badc1b329b408d124e36f08061502d084e52e1dd2f473f4dec417800",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.3.0.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2e4910df1e60432b26474e0fc5c03331a9f0180bdba76955f9565d8f40164736",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.4.3.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9ae90603dcdb1bee5212c5fe2f605ee7d5238b38ef50e543920d0d0fcff74e11",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.5.5.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4c1e1a675e932a2400cfdcf08c90ce0e36ca1324de25b13f03e1aa2ca6156ce9",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.6.2.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5eb25649c23ada4244ec7e0d9bc007946290f4531d73680930643195449e9a48",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.7.5.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "03ed4b823631f289922f92528544c21442923cbe6ac3bacc504b8aac398e8ef1",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.8.2.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d6459d0af5f7ea823c9a9017184db7064a29792e43dee086af6d5582fc58e59",
+ "format": 1
+ },
+ {
+ "name": "docker/sensu-6.9.0.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b06b8318cad1be3eeba445f8976590662f8014e66b61ff6413d566c9df52ad9a",
+ "format": 1
+ },
+ {
+ "name": "docker/ubuntu-14.04.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d14b43d0e006b82b3242dbf025a43bbb63ff63eedd6b212417670b75fea75de7",
+ "format": 1
+ },
+ {
+ "name": "docker/ubuntu-16.04.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7b974549f85535a35f5e774969072d924762b970ff1e795b7d8ec70826ec9a08",
+ "format": 1
+ },
+ {
+ "name": "docker/ubuntu-18.04.docker",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "600a5eb78c7ad219f0fe28ce5681c0d1b03e917781b3353b1c2efc19b71ae4c5",
+ "format": 1
+ },
+ {
+ "name": "docs.requirements",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8dcdf757a8f9dcdaf8a0a95ad6add12a3d8eeae94691bbe85f9a9a5b8f8056f3",
+ "format": 1
+ },
+ {
+ "name": "docs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/Makefile",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e33bf74d59130029ba24168a813112dd923d7044d780ce942db6645d93f56acf",
+ "format": 1
+ },
+ {
+ "name": "docs/Makefile.custom",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2149597a9d9e33ffeeea20bec72388fa42107d543503548d89cab7e3b90c55d9",
+ "format": 1
+ },
+ {
+ "name": "docs/examples",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/examples/installation",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/examples/installation/ansible.cfg",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "563b8ca2cec5bf239ff7e1dd0189adcd0fa6a97aceee6de8bae886403dfe1a34",
+ "format": 1
+ },
+ {
+ "name": "docs/examples/quickstart",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/examples/quickstart/inventory.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a2c708f68e93d0f52b1ded72f78a14ee1ae0f6862eccdeb9438cceac6a52b678",
+ "format": 1
+ },
+ {
+ "name": "docs/examples/quickstart/playbook-5.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e346260feff21bec7c93ee76576dee29914eaea1b09902210f3ebafaa0bcdd59",
+ "format": 1
+ },
+ {
+ "name": "docs/examples/quickstart/playbook-6.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8ab7bf41d5dde10479580d705b8ff2badeebcfcdf76064e900ff7952ad696aae",
+ "format": 1
+ },
+ {
+ "name": "docs/examples/roles",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/examples/roles/agent.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "36e0c406bf4f31fb0dd39ae4b215d7d9d161dc316b1b28cb86da097f9dc62173",
+ "format": 1
+ },
+ {
+ "name": "docs/examples/roles/backend.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b68d0fe0dcb27f5f03fecfd9dd7b8a066be988d6a86e36eaa252aef821cba5db",
+ "format": 1
+ },
+ {
+ "name": "docs/examples/roles/install.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "baa2309b7f3a01e2b7bdbe47d792e552c55ccffe13cc0e88aa58a96b5d44744f",
+ "format": 1
+ },
+ {
+ "name": "docs/source",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/source/conf.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "02cb76c3f0f659fae1de303aebfb88db6c0328c39cccbcbcb6a0cdaf0ed5e414",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b7f7733d08a926b3537ad8e308931d786db6fd0ae6647581044e16208615f01f",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking/docker-images.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "03f249236c97fa4d011b3b4bd59c3a5904cec68e3619e1497103738a07ed5dee",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking/documentation.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "92059787adf3a776f37c5b75c0b0224d0a8003962978a45bcf623881bd290053",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking/releases.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8e30ce3acea64d41c20ade3d02151d8f14e31b1cbec84fc9e95c59d939d572c2",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking/setup.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8f3cfd38a07f6286c7a399e96355b00d82e8eca39b64662fdeffa046e97f4bb7",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking/testing.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fe96a2b58205223dd3dd77134b8da01883635bac58af1b350ff7bee84ff7d3a7",
+ "format": 1
+ },
+ {
+ "name": "docs/source/hacking/windows.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cce9fe33390d8e455c2225622238382e3e71c9ad50e81f8b0df5b5978738a5c8",
+ "format": 1
+ },
+ {
+ "name": "docs/source/index.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c344f74b54edd788efe5cc17aa4cd68051d4c61d766454072f244ccc17835262",
+ "format": 1
+ },
+ {
+ "name": "docs/source/installation.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "77cdfdb6264909ab14ff3d59b110908f4dab01f01f1a4b75827739e2d2c52e40",
+ "format": 1
+ },
+ {
+ "name": "docs/source/modules.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "27160e8c9669767b0ae99c85992f335a6adcfb5bb28278f474a74f332835b901",
+ "format": 1
+ },
+ {
+ "name": "docs/source/quickstart-sensu-go-5.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b8bff4cea65c259a12a56e5fe88c127d2e36c4d9707f633d0834d84a3fe46046",
+ "format": 1
+ },
+ {
+ "name": "docs/source/quickstart-sensu-go-6.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9483d05e50c630e3ffa052b4de3f997945290a937ce0030cc08a7113ae20b047",
+ "format": 1
+ },
+ {
+ "name": "docs/source/release_notes.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2417f24afa8a3a024804075c5c42ecc09ac9de89d85840574b9cfe5180cdaef3",
+ "format": 1
+ },
+ {
+ "name": "docs/source/release_policy.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6fd25c423ac44189c533093827cce9d51d18aac1f35df85df0029edadf16a2f5",
+ "format": 1
+ },
+ {
+ "name": "docs/source/roles.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8354f00bf6d30b5de815cbb6bafa0d06b9ea62848f373df7516e395e0b30aaca",
+ "format": 1
+ },
+ {
+ "name": "docs/source/roles",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/source/roles/agent.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "00fe6a0a69ed23a1247a059d48b9a188e3d654a8ba593d874d7fdc69ecb48ffe",
+ "format": 1
+ },
+ {
+ "name": "docs/source/roles/backend.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "94c3cf1124358f5b025c04debee293a866838c09aa63d68c4c0343d3c61ad36b",
+ "format": 1
+ },
+ {
+ "name": "docs/source/roles/install.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bc0aa89f658ec2a90395f137d82de226999ad4be837034860e3d48b0fbb384e7",
+ "format": 1
+ },
+ {
+ "name": "docs/source/sensu_go_5_6_migration.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "deffa1c41e850c1c7daf86580df9d9c64c02290e7747b06f8c1a53253660affb",
+ "format": 1
+ },
+ {
+ "name": "docs/source/versioning_sensu_go_installation.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "83f27f787298735fb1d940149260c41558e62c51ea005e672ea1caefea076f96",
+ "format": 1
+ },
+ {
+ "name": "docs/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "docs/templates/module.rst.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "aaa2e999c9e8136e7256980601dbd315509fcd8860a5784aa493c9345a690072",
+ "format": 1
+ },
+ {
+ "name": "integration.requirements",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "30a9a73b9186fffdb27b80c7b9cff8d1af86b058ef2dc5ae132368ca57699bd4",
+ "format": 1
+ },
+ {
+ "name": "meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "meta/execution-environment.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "65a3aa1ded18aca17b56c630b9b7a48abdb972f17e67125695f0f048daa94b06",
+ "format": 1
+ },
+ {
+ "name": "meta/runtime.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f47c409467a06433e256cd5f49da832888d307258672da9c7da11fe01d9d36e9",
+ "format": 1
+ },
+ {
+ "name": "plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/action",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/action/bonsai_asset.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1a6b4afc70afe86769663e99956dbaec59c7d7092b8bc2ae79eda3e5544e3b0f",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/annotations.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "de530f111b226d43bfc056f849e10f901d37e68468051fed09f8a6dc1876db41",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/auth.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b44608aa817a9d31eb0b7dc0e1f4f6f5943d7a0c9f8ed5ac4cc1781ba98b9bd7",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e59e1dc61c6784b46fb221819335d5ff0624b5ab6a113a1b68f2fbf2ad97e567",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/labels.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "24a4f982431aa556bba0d825cb9279e8ea7edba278968d0e2f258c78a6e4bcb0",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/name.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a20a214a2cbbd2eaf7bb85b2e0ba9aa4c8d62fe6c23c6e8dec75989bbae3a118",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/namespace.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "65728308f42e3914228ccc1cd9e967cb697f9e02eb373614998d329a62919d57",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/requirements.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "337db3c5881c7bf1adf942df7651661667bd07501d3bdc92991672e56ea23cf0",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/secrets.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "289859549042df6829db33290830fde1f5e407fc11a5abeb7f6f3e0445b7e511",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/state.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8fa51fbe3b600d78e2a7f6a346409f553f9d25ca1229f05c13d59942863afe89",
+ "format": 1
+ },
+ {
+ "name": "plugins/filter",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/filter/backends.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e20a41dd39107122ae71c9d329ba07e11755ae7bd92e3c35e603d99a398a392a",
+ "format": 1
+ },
+ {
+ "name": "plugins/filter/package_name.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "32a8b7ec15fddac13ec5e5550da07785227ae03749febff4126d4c7f3d2c9a50",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/arguments.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a82bfca1d1ea8712676761d79f9691abd845aa9f8f1cd1d8fcd6dcd3b4acda77",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/bonsai.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a15c8ddec407b68279ab34d0de7ff05b5646f75cbd005e53677e09ee60dae7a1",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/client.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3cdbddecb640c0c0a9b5217fa4f8132bc398f0b4aedaaaceba0131199bf75d46",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/debug.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "55d365485cad59f0ecb8c04d4099627ff506212067fdea44691bd740f0c90471",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/errors.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "24b2d5e56b6963c5f2bd88ecff5eea457fdfe5d00bc82ac1b795231a05ad3b89",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/http.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1ab058ee166c85136210d767e4e57195f00a3837a830c743c14b04751cb08c59",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/role_utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dceb65553d08033c7d6e8bef8f524ae2a76c4c82c70e69ede1ba23569c35a25b",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d00ca6a76c92cf855f3710d314831b7c2f22d10e52a72d046ed23e7cbd6dd07d",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/ad_auth_provider.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a52ed13aef2b02642bcec861e5629cd6178aee15de0ae9bf94f406f044cce0ab",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/asset.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "726c579f6cbfe58d9089fe590cf029a0f5aefc23ba3a47cf1ff07b920bdc0294",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/asset_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "46df5f95d354f3b0d653b8b5118483ea6cc08f99be0953f1243fd6bc8c715ea5",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/auth_provider_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d53f358f8cbafdb9b21baf5a5d9f221013a6523cfb4e64eb7e1559490d58c37a",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/bonsai_asset.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dd1724000e3840d6fbcd9cda5b3c6c4784e557485765b78b840edb61c7a40d04",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/check.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0bdb0c4572a20e3898d0a5f82fe8fc08ef4c0d8ff9b502792343b49ed4d81548",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/check_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2c4fe61b4a70cc691dacf3877355d258790e53e688874b4216d6057bd219b7df",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/cluster.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ca9c885157ffdfee4aef50cec7acef89669c54e6dc9de82f41506372877407e5",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/cluster_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "889999db739e76c2c3d2ceec71ac7fd41d568d88cca5de898fbd82e91bf3e741",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/cluster_role.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f70395bbc47164b6bc1a336a2d1b80ac1f2aa1610c408174619752f38a5bc556",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/cluster_role_binding.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ae998eea21e8fd673b282d0242d6323bd3c93562df858404b86c160453e1d7ae",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/cluster_role_binding_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4a551163f5ed2e0bf06e397bd27b713cae52856071048a97fee78755a76b6a12",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/cluster_role_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40d941b24fee942b5b6fa73d33dafa7ed0c3e816880bdcd39fc96b7ee4a25e06",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/datastore.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4a5d49a62b48d571dd49f660e13bb53509421d619a45b9c8a9b3f3c279293609",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/datastore_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0179dfd3073bdfe994622f1a0aa22d250f8d286188a5f47303140924540ac336",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/entity.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "36d6576284dad71da960e144fd0c3450b8520537a1a8b88220d6cfacecf4d110",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/entity_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "93465d322c93a87634fa485987307ad0e6149e2b319ac4559d51036a238cd408",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/etcd_replicator.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a3e17900f134791e7a210b9bb10cb7e08d7342d821dbdcbf34f6b6dea2b79264",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/etcd_replicator_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "20182f94690583b01d86dbb7e8b1929fe78a9c2af9d18cdc32a053c2f6db4382",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/event.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5bd6748018dd45fd614d5598da657f933299242d4bdd59959428cf5a16854393",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/event_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "73f02a988981be0f5c7e8403b4e7f72c7c47b0f5ec3b9c83dd7a1f59b13c1547",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/filter.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bc257b582f6538b191e96aed2162f422c7d9010716d345a9c3afb00ae1f115ac",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/filter_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1ff62d3e629e3676010df6d97ab24919dd90aa8e4ec7ba1e9012bba512397078",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/handler_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9a7bdf4c7fef96782e327070d46c213e98b99b3653c1f65b67a1c8b181d83bfb",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/handler_set.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "23b06861ce894e5aea1d750c99220c6600d6efeffb635cf46517bbdd6b691399",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/hook.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "245dd5fe347a7ea5922181e5b71a8a7bd58f43f7c143a958871d55469f185f6d",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/hook_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5ddb5b7239f4488fdfe08762ba5ea8aeb1093f752aedc027b9f29a0cecd3a818",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/ldap_auth_provider.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d095ad95ef74cceda34d46de7113dbb3f0fd6c4e54fe9afc6e23edab3d96b62d",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mutator.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e9508e0bd9ec5688474220abe6e67c92f7252b27c80060bfbf98981880a3408f",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mutator_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f51f88433a25ae292239fdc6a3391a83e0da6feaf9a8c856d792455de020fc05",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/namespace.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b2a549f56e7fcabdb669770da3e552549d0cfdf49e3f5649004a5679ad5587ca",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/namespace_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5eb7345ad6a316456e65f7b6c977df0792f38619e7f8f4e72cb38fdac977cbaf",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/oidc_auth_provider.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "52d850b3d6ace1be7bb8bf7e92e31271002c9ed6aa8d8ea56e2af4c980e33201",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/pipe_handler.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3dd56750eabe9fc6490307117daf88fa9ba685673c49f0b5af3b9d387440e7fa",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/role.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0aff9dfdad3c77a57cd3faec89612dbcd5af78efa610146f930d86f1e31f83f0",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/role_binding.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16babc38cb70db50f4a6397adf24cabc5a2611291d8141756e8a50805bd06491",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/role_binding_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2d74a32830246670496043c40437c1dac025adcaa1877f366f7dbfa431f9e47a",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/role_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "38b6952d82b0a614c752fb9d11921885cd40fa4a96950280bfbfee0ee82c8b56",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/secret.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "61bae955742a34b16dec2b788191c2a50ef53a240fcfd1d39336b229a6d7b69c",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/secret_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "24781ec21d4fa0aa9196404ae2ce536e34ea74df156de06deb9f09c94a9d8eb4",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/secrets_provider_env.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ee050f7c54dbe7485717bf2820b3967679ca1f00d13523bb3c0a0472f51e8c19",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/secrets_provider_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2e9af254afc7b4ebee3421cba06721bf5848af7d25ae8e6a2a11a4a2be067ef3",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/secrets_provider_vault.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ce01d02aa45072f863274ffeaf83f607c9f2c78f3a56744698864e9471d85ce1",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/silence.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c06398c67b6192664d1eedf7743807500968a558548a0f4558df6fce0c2bf07c",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/silence_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3b0d1e7d8ed423dc8b7f6c7c98e0a83f46075b6887a245edb15d3593e2b0d002",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/socket_handler.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a4b440a8f143696302d5cbf3f8edca4ef4f438604196605fbdad9d1066dad07e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/tessen.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5128759afadbba3edc7ed18dfd91176a86cda8b86bf2e283bc1509f9b58e2d82",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/user.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "973ab65cb2cff9083dd7a5f2164571d34d7981c306c7957c31f46565d99b419f",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/user_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4775877c375a1b546b9bd49de726e6f96d72d7947f098e801210cb8846357ae8",
+ "format": 1
+ },
+ {
+ "name": "pytest.ini",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4f2478cdf42d7e2a9493090e9aa62a058ee3a368ba396f03c1fe464728f089bc",
+ "format": 1
+ },
+ {
+ "name": "roles",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "06dca09fc919a0aa297410a2de953d9ed9e669d0ab929ec7537b04742c9cf574",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "33f01b24e393cbb7e3eb32e16ad1e916ec6ba6e436dfcdbf5a1c457ce6238fbf",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/handlers",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/handlers/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ddeac82dc681335eb889c0030ba7927b4e8c06188428218e86f058eddd3214bf",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c365bbe5dc27a54515a79d5794b1df7ab82ba858d2d4fc1d52ca4de1a8d5ab65",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0066c8a5fbc6b76d9fa8ec9505141ab99982564a64a64b6d1c46eea0a316732",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/configure.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1a9e48b1277b28952d6efa8bb45f024b7ab41942aa63443b9c5bacffff4feb85",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/linux",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/linux/configure.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5b89b4d30cbd3534cc498f8eee583898aaa893003e4a4528085ef7cb5e91ce71",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "01a7870ec401f3fa5f42b692c1441672d31d3caa2694c6367e15b980c463e1a0",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/start.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5bf242f7f5a50861b0cc0432c350cc0153eeaa1692434159de0fef81dbb641d1",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/windows",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/tasks/windows/configure.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "164820a1d9481a43d2b88df2c3955c2d2a78d9dd50ef8d7db3fbe5a82382291f",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/templates/agent.yml.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cdf241ee60067c368fce4cf4b5c47311428d940dd2c404ee60fd5485f6614878",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/agent/vars/linux.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b5e9a4b8caf6a83b515dd2e642dcd105ae17c36382f50e495b48daddf81c1e57",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f88fc6f5f0fcf83dd503713eddda7c76e2da6de261ed6626b094b732cd3ce512",
+ "format": 1
+ },
+ {
+ "name": "roles/agent/vars/windows.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "36b7a1dadb91178a78b7c3e4aded558a0700939c0e99e0d9f1ed998585a40f69",
+ "format": 1
+ },
+ {
+ "name": "roles/backend",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "abed04af924e7b21fae4477c9173d4f73d92aa094cf8ffa8b88b8a7f79fe1471",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "69b3e10221482a07e06c040274ba753082fcec2fb2f1aab09568faff82f0ae62",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/handlers",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/handlers/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d14dc66fe9244d03183fcab3c4e541788db284f4867b645c76dcaefdec0c58a1",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "28af81d29571f849b58dad7e667b86d3c687825c3618e67ba2df1d2327da8d60",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "63f743937b9ac5934aeec02276ac957df1938266a31eab041e409e169547a884",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/tasks/configure.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e98b97ba542db5c359b1beb3fe1f71aeb1785fc8bfb2edf5aebdca3185e4b9a4",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2b5d73651c0a3d0be6d4855e392273b11af3b579cd147f05a5b7d2445793636b",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/tasks/start.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1d5699a6e746a4b65a58787ec503840e38b5dae7370b5aad8c149af57b68611a",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/templates/backend.yml.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1b5232126bd8ce881d4c485c316a1277dc517d6854c8845140bcc9a5417550f1",
+ "format": 1
+ },
+ {
+ "name": "roles/backend/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/backend/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f88fc6f5f0fcf83dd503713eddda7c76e2da6de261ed6626b094b732cd3ce512",
+ "format": 1
+ },
+ {
+ "name": "roles/install",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6a2e966b8223c0a1a7537bda83d6cc580306e4905c78a1f203b69743ee847a5e",
+ "format": 1
+ },
+ {
+ "name": "roles/install/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f41fadb8a7ff1dc31113590444ea5704d77beb8771cc09f55993c92c469b6613",
+ "format": 1
+ },
+ {
+ "name": "roles/install/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/meta/argument_specs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "151b93a5fc4edc8cb4cdef0b28daf6226ddcbd88227cf85f92f43bd38b9c4dc4",
+ "format": 1
+ },
+ {
+ "name": "roles/install/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f421fc4b8f713f9f11290bbff7ad2cb90d5f78d83309e1e6c7b683fa6e24e73a",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/apt",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/apt/install.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "898d2b4b57b332107c48dfb40dcccc3d40ea7b532eeaca4c9c201dc3558f5ee7",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/apt/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "547a5982a060392175b4e0981da7435add9b9471272fcf6d7163b6f1ab618b0a",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/dnf",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/dnf/install.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3d4519cef82e17225b4e82b72aba41c796cb8dedcde35412edb9e1b0231dfa88",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/dnf/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "87483a599eb30e4b21649ee15f3b772430f5f9fe4f801e27a3e590acbf6e96fb",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "79065db24a92c889558d118f4b3479cc5cc479a7f4cb82d336d058d8cf4c2417",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/msi",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/msi/install.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ecae9c9ff9b3365c5c1c51e4ba93871adfe6c8058fd322eda65f9d8cfd6d3314",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/packages.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c5cf23a3b44d5babcb0535c12d458031829c5ff9d611168934d7c52beae578a0",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/repositories.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "88334da8f1e5d828e97b05ef7157095fe2c661698566b85407b6b74bb0fa22ea",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/yum",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/yum/install.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "332bc4ce394890cb905ae28eae3592a94ac01ee53d15f3d411c63d65952f65ac",
+ "format": 1
+ },
+ {
+ "name": "roles/install/tasks/yum/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "87483a599eb30e4b21649ee15f3b772430f5f9fe4f801e27a3e590acbf6e96fb",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/Alma.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40c19f543bbc4ec7a040d6d6ffea0ca3104e92169e1006948c1c35dc97e7f2ac",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/Amazon.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d5fc0a9ab57713b616ffd1c19ab046acafe3903ba5816763e5867732313687e",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/CentOS.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40c19f543bbc4ec7a040d6d6ffea0ca3104e92169e1006948c1c35dc97e7f2ac",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/Debian.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "821d50ce3cbf271104507a01e56aa597b349ef1b82cd4426712b3ed6544207d5",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/OracleLinux.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40c19f543bbc4ec7a040d6d6ffea0ca3104e92169e1006948c1c35dc97e7f2ac",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/RedHat.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40c19f543bbc4ec7a040d6d6ffea0ca3104e92169e1006948c1c35dc97e7f2ac",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/Rocky.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40c19f543bbc4ec7a040d6d6ffea0ca3104e92169e1006948c1c35dc97e7f2ac",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/Ubuntu.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dd63d426d344ec5ba922791b9bc0d1af5911ae3c87318a594a29222091da1496",
+ "format": 1
+ },
+ {
+ "name": "roles/install/vars/Windows.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "27c41ebf0fc68db661eaf70d17d0149634dff805081e8bd8d530443a52514ca9",
+ "format": 1
+ },
+ {
+ "name": "sanity.requirements",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bbe597d4a77973f8ab0f7eafd636df5b4b530f346adbf69afa11e0fd68d58e6c",
+ "format": 1
+ },
+ {
+ "name": "tests",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/config.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "af61b7b0c3206599d92a45abdff0b6fbc3fc0f30b88216688446b3eb8568b316",
+ "format": 1
+ },
+ {
+ "name": "tests/integration",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/base.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1f2c5d511980f16cd927ca1fb52a0f3e601650165ecc202c6a92c6651bad9004",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/action_bonsai_asset",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/action_bonsai_asset/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e2dbfa5a6e7be57ad70994228ffaa50f3763429abe27b9343af0f042eb9fa5ef",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/action_bonsai_asset/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_authentication",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_authentication/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a103594707ff263ec4775108b3885d40e3ba66181548ab7ff38c6427f839591b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_authentication/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "178e6ea931d9385b4dfec893dba07bdf94dd71fbe1de003ccd1a0885c92f5a82",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e4d689d76ec95c48b36ecfb982e943b2fbcab7d4555e210afa5ac1ea86c9ba58",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files/regenerate_cert.sh",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e1cbeee84e2890ed38e8118a86d71b2357a3086e399c0d18847cb7219a7f8942",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files/sensu-api-ca.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6e15983981fc8c3353f5edb949594df0f127db756e510d965674da864cab4a22",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files/sensu-api.cnf",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2814df5948b90fcdbe7d9c7bf94da189df46acf958296ccae967e8a516c3f026",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files/sensu-api.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0bf1aa2f71222da60bff2696da9466cc90141827c25f34dd41d769d8880cce33",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files/sensu-api.key",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8cb76f736976fa2e46783377825eed07c39a08eca05bd52a7e0b37dc0ff64801",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/files/sensu-api.pem",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e4ebf381f9356973875265175984c5042503e6ed797e4082f14df6acdc16705b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "40408ccc064612c6d4f2651d4cdd58cd2f080c39edcf91e9ac7dda72431b4a44",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/misc_api_cert/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "838fc000b3a0f408a84e701f276a749520f0db6de4cc6488bca1da2b01a7a0f6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_ad_auth_provider",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_ad_auth_provider/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ec0284630a467d5b3d5f05399a82b3ad3d335c0696a21a84772912d16ba24a95",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_ad_auth_provider/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_asset",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_asset/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2b6f8db8c0524212e095bdb991b16c59898dcdd532f64f9c4da460abae005e5b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_asset/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_check",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_check/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cdad4b4ac55395074b2274b1b69a7593a97491185b862d99fb612cc62da5e72b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_check/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6d1ad6139c292c2480408c473fc428277c4816ee548914eed6f87218cfd8250e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster_role",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster_role/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f5983392e530d703f4abb1e630068fd417681ad55fa114d3effdebf6e20ddcdd",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster_role/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster_role_binding",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster_role_binding/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c2a2e52fda050054c7cdaa5dae37ec6ece94f655dbf6ae2a49f3c401ea975c20",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_cluster_role_binding/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_datastore",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_datastore/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1dfffb92990f266461a0be38b96ad53007217ac142471ece37f5a72fb54a157d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_datastore/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_entity",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_entity/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "73c21f1f83697ca17523bcb184a58ed035795326c02ca8eb3c6b4607a6c68be3",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_entity/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_etcd_replicator",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_etcd_replicator/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0900748b08ca92df2e4db5fa8898c36d5d01a1bf00f8c74eb06f1d999a6d7210",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_etcd_replicator/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_event",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_event/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2d688472ed115932914dd5a4b03f288522f1f5951dd6e14aa7920c69907cf049",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_event/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_filter",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_filter/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e0052d11b6e228ff81dfa3dfe9335003381d31825bbcb01a0dc837c4c0db7094",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_filter/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_handler_set",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_handler_set/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3997eda49454c728c35d7598d052af3cb9d765dcd3773b309d8c01be09205f05",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_handler_set/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_hook",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_hook/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a71edd518ba58a6b65a9ea8556f074f4d667ea7e0d7d62daa645fcbbd17b4153",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_hook/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_ldap_auth_provider",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_ldap_auth_provider/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6bf3ea1c6a552d955500c71216a883eb0882e0ebd70f39678a1432400f925f53",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_ldap_auth_provider/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_mutator",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_mutator/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fe887e75c4eb72ccab90df429255493e74f756d02fd6d86848400210e1cc82a2",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_mutator/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_namespace",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_namespace/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d9acc623aecba6078a92642ca1589934731ad8e8b7d4a1d1b2db207b753e636a",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_namespace/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_oidc_auth_provider",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_oidc_auth_provider/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "38beff2e4a2d5ae16f629693049a66aed52be97bea8388e14ca44f01df71862f",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_oidc_auth_provider/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_pipe_handler",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_pipe_handler/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fcc73e6d100431a5c574436eb7ed4e4a39ac0fdb97da1fa81922d6d41c26cf13",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_pipe_handler/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_role",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_role/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fa4458370a2096eb4e17ab87211fe21f8674dc3e4547800725126d5db0f8f8ee",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_role/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_role_binding",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_role_binding/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "737ba36bda11bf0e6b6b0fa78df38f25e8b13562586b1c5a83856badc2814308",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_role_binding/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secret",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secret/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "86c3512a17b4fc59ce1be531d9555ec5d8e4d987e6cb885b23364769588a9128",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secret/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_env",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_env/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f87d981967990828c647e9cfeee55d41773965da176f5f3252e2401254eff587",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_env/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "71dafe888fba8c7b21fae6565418085c3dd9eb9a286395098ff20b554c6d5006",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault/files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault/files/ca.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fe7aebed0559a563d303ad5402661734500bf759b541f5604aabc6f2784682fb",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault/files/client.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c45c551e047d0e4503f842b3fd74b9d688352fb18c39d415f91639e52bad7104",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault/files/client.key",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c814aec9b7f3147b1e95e0cad0520483ccf584c0b39da372749dd90dc5ee4a86",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_secrets_provider_vault/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_silence",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_silence/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "adf96830a4733c69f56bebfdf6dce57e7cedab88293717195b66af220c8d8f2d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_silence/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_socket_handler",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_socket_handler/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78651227496b976709bf419da5786798480534c8bb163a2b990c3b0a05e1198c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_socket_handler/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_tessen",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_tessen/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e72c0d42335048393941d38135ca5d3c2432cc457d144087febb328229fa96cc",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_tessen/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_user",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_user/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8ac86c519df0b738354382db5c6e5da8954affaf4d05bcc38cab98bee0e5ff9",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/module_user/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_config",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_config/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8f08b5e71c731c4cde354962cde92cbecec4706d0ebcabf94c671050f6a52de0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_config/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9c426f8a43dc7873bfa6b612b3ec4a7202048bcfac7aa3bf6a1e43e6f7309004",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_config/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16470c6cd0197ec6fe497f8853e74bef3103c61eeabf4ab5aef027b622df50fb",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_default",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_default/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d25cbe6afe40aef0ad685ae0c2107ff210096430ae169926c454c266f312e630",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_default/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ed9d8cd8d7d34d989588138da69b31b8796cc44fd91d2f4a3fc092c0c44dab83",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_default/verify.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "85fe091e6356f0cdd69ebd8c72021861fb3bba3f0959ab01d04e2abf4859cc61",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_secured",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_secured/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6a51e6678ebce349d6aea2639a6cbf4b2151cb152230040746a11096b5f1b498",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_secured/files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_secured/files/sensu-agent-trusted-ca.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0cc89bb8e83a75c0a17e07039d97801100210eecaf83ba123ad751d31e6c8a5",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_agent_secured/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f5177a971e90cc7c13588e447b22452f1a33a94ab25e151d686d6bc17a01b690",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_config",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_config/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "73163b370569fd1c63f5932de6777d051c38d8c752230f300c2c1d0f2aa092cd",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_config/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9c426f8a43dc7873bfa6b612b3ec4a7202048bcfac7aa3bf6a1e43e6f7309004",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_config/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16470c6cd0197ec6fe497f8853e74bef3103c61eeabf4ab5aef027b622df50fb",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_default",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_default/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4d481df5407ba137a0940116b8f61c1747d9a3ea8b3e85784aa407a853bfd5aa",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_default/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "da4958f5d8476f6fbd162618b6b619a2701a45d2c4c44796bb5f1439233f1e0c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_default/verify.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2ec3ee5184c79e85939d3a870ace1a1f3c25592ec3d87f3184b2da81c433796c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "42b0cb4ebc8220516575ce6cc8bca826ccc24e54068a83b253028f0dbdf8d09c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/client-ca.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0cc89bb8e83a75c0a17e07039d97801100210eecaf83ba123ad751d31e6c8a5",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/etcd-client.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "91f1ad5e44a2ccda387a4fdb473a6b1eac220c0dffe7ed786de575d8b0ec89cf",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/etcd-client.key",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ef8116f17b7fd958a579c58890af7bfd80f16e168b19aa8b7f8c402578ab7e7e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/etcd-peer-ca.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0cc89bb8e83a75c0a17e07039d97801100210eecaf83ba123ad751d31e6c8a5",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/etcd-peer.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fd16ec3d07a7cdfe74910e6a769b4fe5cc73b7f5ae49b22ea53fee53ad62ebef",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/etcd-peer.key",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4f4b32ab8dd49b9f2a64c6c6600335b242d100f7e722cbb87efa427732af7aff",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/sensu-api-ca.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b0cc89bb8e83a75c0a17e07039d97801100210eecaf83ba123ad751d31e6c8a5",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/sensu-api.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8148d3f7b944ff7feac4ad469114c1779e2967aa33b9f58eae7ff42f204549a0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/sensu-api.key",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "543416d24894fc79aa28ec6d021dfe190c4cc94ed8939cc55d35779fb2808c84",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/sensu-dashboard.crt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf6928ab177a3df13777cb16d91db990a5771a7bbdb6464aa103dac948b4958d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/files/sensu-dashboard.key",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4a40d9838c79807115f56c9d5b40e6a212fc1c159aa7bb696d1b4a64e232bb2c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8b1c3eed7bee4ced542d603022ec774aea5ec75ed2dd618cce02b41269d85897",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_backend_secured/prepare.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "16470c6cd0197ec6fe497f8853e74bef3103c61eeabf4ab5aef027b622df50fb",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_custom_build",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_custom_build/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2ea525a1ac3c38143e65fcdf208471b53b8e76c6a599e07c712eca8948411499",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_custom_build/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2c2be29eaf5026cb751a24284ace2e84a0172460f929008a7267bdfab9d77991",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_custom_version",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_custom_version/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0cdb03edcd5234b9ba5dd6e186c2a5ca0efe817f78afd2729b919322f0cbfcc0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_custom_version/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2c2be29eaf5026cb751a24284ace2e84a0172460f929008a7267bdfab9d77991",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_deb",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_deb/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "09c96f594df40aa4594df24eae84a309a41078f2b5964adaf88650961eae9786",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_deb/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a064a3118dae91ed67b5e0c57336439209a3ad34304c8ec7dbb97b3b750c52f4",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_deb/verify.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c4d0513f413851eaa1471d9b809066e36ee035d12fc358311955ef32d22f694c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_rpm",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_rpm/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "09c96f594df40aa4594df24eae84a309a41078f2b5964adaf88650961eae9786",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_rpm/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "73e95af177c03d817a225c1ce68cdac372925249d89a6968041a71192feb1c6d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_default_rpm/verify.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c4d0513f413851eaa1471d9b809066e36ee035d12fc358311955ef32d22f694c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_downgrade",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_downgrade/converge.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d87274c5bc8f0c3922bffce0baf273392b87fb6cfc0f82d4211f9e33615a5884",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/molecule/role_install_downgrade/molecule.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a14d6db1aa03cf0a5f22d94764dc4101af7c070108c69c54610a40db0d3366c7",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.10.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8902a1d1eea3b9bae32533cecec1bf7eab225616092b4a045c68a97a3cfd236",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.11.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8902a1d1eea3b9bae32533cecec1bf7eab225616092b4a045c68a97a3cfd236",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.12.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8902a1d1eea3b9bae32533cecec1bf7eab225616092b4a045c68a97a3cfd236",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.13.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8902a1d1eea3b9bae32533cecec1bf7eab225616092b4a045c68a97a3cfd236",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.14.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8902a1d1eea3b9bae32533cecec1bf7eab225616092b4a045c68a97a3cfd236",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.15.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a8902a1d1eea3b9bae32533cecec1bf7eab225616092b4a045c68a97a3cfd236",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.9.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "de0a1d28d509982ef8ff2dc7216966ed6b3008ad60427058a52b4e1b1d86e4b3",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/validate-role-metadata.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5e06455bcce84f42f0ad39b10c375876390c859dfb2556ffb7f965868d2e9b0e",
+ "format": 1
+ },
+ {
+ "name": "tests/unit",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/action",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/action/test_bonsai_asset.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "871bc396c94e567a91b05c54a5ad16ab5fe292cd44769a0a153ff8bf1dc98ef6",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/filter",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/filter/test_backends.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e7cf82138ae1a81dc5d0ce6db0c7fb789bdd9bcc77f72d50ba2efd159088c4d1",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/filter/test_package_name.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8c1635b5e7fa4647c9178bf8502e841baef47be79dce0757d69a54dbacc65d37",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_arguments.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3121f352128997e9ea58abbcf59461d650b521ac8850215df6d45f33a5215168",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_bonsai.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7ff1c4f9860b3da9fb53b6291588c8c1c5c07bb12f6e1c45c80bda67f7f43c4a",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_client.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "addfba5e04ae22eaa7119df5a932aa75f8b2fae5e41d1cd2e4ff58e06d41d7bc",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_http.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "95109bea3f5e9bdf9b8ffa5859f199d15df613519f3bac4fe442bbfb2252e9ed",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_role_utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "93fee68e8e4c325004e0ad1a63871607e6c6453cc12cdb3871c1537db09b638d",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d08afb7804ff3f8b6027943c1f65009938c3293e696c796ad943fe4fad427861",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/common",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/common/utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9eccbf044ba71f3b9f2e4c8c86655e750d0abfb746f250bcb15cd56546590cbb",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_ad_auth_provider.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "88d306d1b3342477e23a74093718a485771270e80e975cef597f2d347813e4e7",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_asset.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5acfc811d1996e922e0da03704ff70c0bb0f16783810c4c26425552c3a26938a",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_asset_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a16e67d47c994d80a5853488600dc8a8b92ab4414783073a7aeff8eb579aef27",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_auth_provider_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7c0613ee09762fce2ca4a4586780875d1e677a5035a3027d85e209a456527ddb",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_bonsai_asset.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "82136ec7f29b91f1fc3579bc7fcd89e6ad04b61bb83f9e973f34c0e3c27e1266",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_check.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dc90d0ecd02b29aaae71e2243b072cf74dd716bc7310465d16e14d1bfe78372f",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_check_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eee13871e171c0794a59997220a459a860bbd61556845f6cf96719ddb5c79ab0",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_cluster.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "061a44ec3c0655853a136e01a7f4dd5d81e0d6866547e17e428f0001bd6732b2",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_cluster_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a3899d5ec41b9bf2bf1fa906667a00797bc8ce330a2a449ae7a423e055f52077",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_cluster_role.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ba3a056f56e4423245cbf08ad5603d3ab9f4abe685e208e4db96ffe33da42983",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_cluster_role_binding.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "de16415913adc61ac7fd4e53caacefdd1a43f58e4b5240ab15ec2abbfc525423",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_cluster_role_binding_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5dbfe0ca293d3b9894446c5e05974e1b51187b217d1d634db01d58bbbd06b583",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_cluster_role_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "379c271dc5dc1f68cffeaed9fa23b773cc68a6478dd2838f697a21f89ddcf0c3",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_datastore.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7c89afa9b5416779dc0e39fbac35a6b6834e805fc771c041d27425351e41deab",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_datastore_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "84a70506c2b917c456ba6ad8ecddf68e197b14b9d4eeb3964ad465a46111a490",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_entity.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3bc056f5759661d3dd497e1a17274d3de5a9e6c1be2a184b8debb213d82161ea",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_entity_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f7c84f01c9e6b12b7206c3762adbb2e7bedcdd9fe7788d53f9952b15039e68d3",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_etcd_replicator.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "393df7ccfc9dc9b2fa8098ac475e73594a9af918a324eb76c22be583a3529b93",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_etcd_replicator_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c96e92d05da16572893d66419fb16bbfed6e04cfa02c5e3709125fd84bdf5437",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_event.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e7c6b4e8b1dbde9ddad3de7e8f90f0916503936d8b8c1f1913d7508d6aa01409",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_event_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8fee8e2246f74a61a7cd1913d41298172033149218490b6d824ca7a95ae477a1",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_filter.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "84dc4c8b98d91c0b768ab4829dd5355dc1134bd03a7ebea35bd48bb2042f6000",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_filter_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e44e8b46140789b0d83d186a17423be9e30e4e8bffc3b1b4ca1c0a20153743b9",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_handler_set.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "92d2ae7a0fa1859e8955a6e4c789188b6316155f94978ebd17ce2bd19bf274e9",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_hook.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fe07af2e371eb273df4a04b614144d1e08b6d8baf4fa08d9210bb4249d7574e3",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_hook_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a847854deab3a0e69022ac957f666b372baa1ef6eb7feebe918dcf4e4b93488a",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_ldap_auth_provider.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "b7bd8f8d935e45ec68c3e932c0dc06ad61cb0dee5a8a4ddd05106cf20442d437",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_mutator.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c4e6d54b436549595e3a9a817075e53a211e59aab55ac23ced055fc4c4626589",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_mutator_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9925a199b42447ddfade5ed36a2f8e453efd10119edc8fb2a5b00b3f322f0edb",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_namespace.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1c6c0516244674a8650efe22776fd33e57badaaa064e12b1ed5cead96900bca2",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_namespace_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cfbd0530d91f6b2532773b78480a87180433f2fc06db3f73d2fa368e62304b95",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_oidc_auth_provider.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d91ceb486420b9efe8f9ff2e829d92a8773d06b780a1a0f6890d8290e47ae6b",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_pipe_handler.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "46528d93c9379ab4b16aae974fb19b4c3db03750b050044f7253147182b1c051",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_pipe_handler_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3ca59a788ddc46f76bcb965912ebe31854ffcc295fcbe2d2be7ae6a25d1e2df1",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_role.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c33cce895e0a209a7c3e9bf634fa49294df6a659db1198355deef181c5e2c97a",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_role_binding.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "feab97eb2ae29cc6c24d91ec4b325e4e9aec2b5bac41ccf9afd5fa269029ead0",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_role_binding_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bfaadac39deff693d35619eebd7f72b65ef111423bebe3bba941247903d83a69",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_role_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6a179d1ab7fa5aa18873b7b85127ac620b44f632fb8c542ce25c3d25192e7f99",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_secret.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f2b72c8660b78b33f3acd1aa7ff874cd6a104bc6391d8d56430c2ce780205c1f",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_secret_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "14c1c450ffbb6e25df0c52101c6b57baeb1b10c256f6552babac1b52a3a9a8d1",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_secrets_provider_env.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6c99934039a87a12663aaaf42ebe4677f5ce6643e07a8df7821938b8f911c083",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_secrets_provider_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5f7bbfecee49ca348f60862234a440a09eeb80e3f5a7209a73de32cc1fb49627",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_secrets_provider_vault.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "290c4d80e6722528598cd4bedfea312e38780e0ce33c39d7700bebe6171429b0",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_silence.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e9b2c9f2886bfd3e905eaa0dd8b36e90427d7693533335acbfeeffa6cab5bff4",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_silence_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "17253081f5391d67801ee3bb2bad00380d796026a82e0c3fa7a71e932819f34d",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_socket_handler.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1c6c8372257d8cb23635ca014529ed44547184c946dc6708761e4faa9d854e99",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_tessen.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cf15938a12906fb6e128368c86a29c159704b704ba720fb4cde60c7a636807c5",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_user.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "69831b92a8d7aa676811ecf8b96f25dd87412442412f4a37f47041030b639c7c",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_user_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a86969539a25edc7b257f3f6fbb6d66bbbd9c5ec056a225ab50afb435fca69f0",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/requirements.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "06447fa81cc36d2bb270ccc7584d6fcd7adf61e700cd456642ce86d82ba4a8d2",
+ "format": 1
+ },
+ {
+ "name": "tools",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tools/windows-versions.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "9f0e1b109a07d031b7741626fdb5efb11a06cd32e26d9faa6d1689ec2fc97b9f",
+ "format": 1
+ },
+ {
+ "name": "vagrant",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "vagrant/windows",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "vagrant/windows/Vagrantfile",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "577e722a616b4460c8616a3e157dff8bd9b0797c13f45746524aa092e41281ca",
+ "format": 1
+ },
+ {
+ "name": "vagrant/windows/back.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a77bcf415efb41013cdd8c5d792e58e204bce9c7fb2a00a54782d21290a22fde",
+ "format": 1
+ },
+ {
+ "name": "vagrant/windows/inventory.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f77cdd5742fc469605610da3a8477e9774e3ef1540542680517a108837911c23",
+ "format": 1
+ },
+ {
+ "name": "vagrant/windows/play.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "18933fb8ae7cc0906993f601b29f0898107c0b36204eb56d4dc7f14ff4bf480e",
+ "format": 1
+ }
+ ],
+ "format": 1
+} \ No newline at end of file
diff --git a/ansible_collections/sensu/sensu_go/MANIFEST.json b/ansible_collections/sensu/sensu_go/MANIFEST.json
new file mode 100644
index 00000000..71eedb23
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/MANIFEST.json
@@ -0,0 +1,33 @@
+{
+ "collection_info": {
+ "namespace": "sensu",
+ "name": "sensu_go",
+ "version": "1.13.2",
+ "authors": [
+ "Paul Arthur <paul.arthur@flowerysong.com> (@flowerysong)",
+ "XLAB Steampunk <steampunk@xlab.si>"
+ ],
+ "readme": "README.md",
+ "tags": [
+ "sensu",
+ "sensugo",
+ "monitoring"
+ ],
+ "description": "Roles and modules for installing and using Sensu Go",
+ "license": [],
+ "license_file": "COPYING",
+ "dependencies": {},
+ "repository": "https://github.com/sensu/sensu-go-ansible",
+ "documentation": "https://sensu.github.io/sensu-go-ansible/",
+ "homepage": "https://sensu.github.io/sensu-go-ansible/",
+ "issues": "https://github.com/sensu/sensu-go-ansible/issues"
+ },
+ "file_manifest_file": {
+ "name": "FILES.json",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "392d5becd5fe7bb69c6ad48108abdce5cfb54eb9791584d189e7783c36e252d8",
+ "format": 1
+ },
+ "format": 1
+} \ No newline at end of file
diff --git a/ansible_collections/sensu/sensu_go/Makefile b/ansible_collections/sensu/sensu_go/Makefile
new file mode 100644
index 00000000..abadb048
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/Makefile
@@ -0,0 +1,83 @@
+# Make sure we have ansible_collections/sensu/sensu_go as a prefix. This is
+# ugly as heck, but it works. I suggest all future developer to treat next few
+# lines as an opportunity to learn a thing or two about GNU make ;)
+collection := $(notdir $(realpath $(CURDIR) ))
+namespace := $(notdir $(realpath $(CURDIR)/.. ))
+toplevel := $(notdir $(realpath $(CURDIR)/../..))
+
+err_msg := Place collection at <WHATEVER>/ansible_collections/sensu/sensu_go
+ifeq (true,$(CI))
+ $(info Running in CI setting, skipping directory checks.)
+else ifneq (sensu_go,$(collection))
+ $(error $(err_msg))
+else ifneq (sensu,$(namespace))
+ $(error $(err_msg))
+else ifneq (ansible_collections,$(toplevel))
+ $(error $(err_msg))
+endif
+
+python_version := $(shell \
+ python -c 'import sys; print(".".join(map(str, sys.version_info[:2])))' \
+)
+
+molecule_scenarios := $(wildcard tests/integration/molecule/*)
+
+
+.PHONY: help
+help:
+ @echo Available targets:
+ @fgrep "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sort
+
+.PHONY: sanity
+sanity: ## Run sanity tests
+ pip3 install -r sanity.requirements -r collection.requirements
+ pip install pyyaml
+ flake8
+ if which ansible-lint 2> /dev/null; then ansible-lint -p roles/*; fi
+ ansible-test sanity --docker
+ python3 ./tests/sanity/validate-role-metadata.py roles/*
+
+.PHONY: units
+units: ## Run unit tests
+ pip3 install -r collection.requirements
+ -ansible-test coverage erase # On first run, there is nothing to erase.
+ ansible-test units --docker --coverage
+ ansible-test coverage html --requirements
+ ansible-test coverage report --omit 'tests/*' --show-missing
+
+.PHONY: integration
+integration: ## Run integration tests
+ pip3 install -r integration.requirements -r collection.requirements
+ pytest -s --molecule-base-config=base.yml tests/integration/molecule
+
+.PHONY: $(molecule_scenarios)
+$(molecule_scenarios):
+ pytest -s --molecule-base-config=base.yml $@
+
+.PHONY: integration_ci
+integration_ci: ## Run integration tests on CircleCI
+ pip3 install -r integration.requirements -r collection.requirements
+ mkdir -p test_results/integration
+ pytest -s \
+ --junitxml=test_results/integration/junit.xml \
+ --molecule-base-config=base.yml \
+ $$(circleci tests glob "tests/integration/molecule/*/molecule.yml" \
+ | circleci tests split --split-by=timings)
+
+.PHONY: docs
+docs: ## Build collection documentation
+ pip3 install -r docs.requirements
+ $(MAKE) -C docs -f Makefile.custom docs
+
+.PHONY: clean
+clean: ## Remove all auto-generated files
+ $(MAKE) -C docs -f Makefile.custom clean
+ rm -rf tests/output test_results
+
+.PHONY: check_windows_versions
+check_windows_versions: ## Check if our and upstream versions drifed apart
+ tools/windows-versions.py check roles/install/vars/Windows.yml
+
+.PHONY: update_windows_versions
+update_windows_versions: ## Update Windows versions in variable file
+ tools/windows-versions.py update roles/install/vars/Windows.yml
diff --git a/ansible_collections/sensu/sensu_go/README.md b/ansible_collections/sensu/sensu_go/README.md
new file mode 100644
index 00000000..82dd241b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/README.md
@@ -0,0 +1,26 @@
+Development: [![Development status](https://circleci.com/gh/sensu/sensu-go-ansible/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/sensu/sensu-go-ansible?branch=master) |
+Stable: [![Stable status](https://circleci.com/gh/sensu/sensu-go-ansible/tree/stable.svg?style=shield)](https://app.circleci.com/pipelines/github/sensu/sensu-go-ansible?branch=stable)
+
+
+# Sensu Go Ansible Collection
+
+Sensu Go Ansible Collection is a bundle of Ansible content that we can use to
+manage all aspects of Sensu Go. It contains Ansible roles for installing and
+configuring backends and agents. Collection also contains a wide selection of
+modules for runtime management of Sensu Go backend.
+
+Collection is freely available on [Ansible Galaxy][galaxy]. For Red Hat
+subscribers, this collection is also available on [Automation Hub][hub].
+
+ [galaxy]: https://galaxy.ansible.com/sensu/sensu_go
+ (Sensu Go on Ansible Galaxy)
+ [hub]: https://cloud.redhat.com/ansible/automation-hub/sensu/sensu_go
+ (Sensu Go on Automation Hub)
+
+For user guides and references, please visit [the documentation site][docs].
+And if you would like to help us out, check our [hacking docs][hacking].
+
+ [docs]: https://sensu.github.io/sensu-go-ansible/
+ (Sensu Go Ansible Collection documentation)
+ [hacking]: https://sensu.github.io/sensu-go-ansible/hacking.html
+ (Developer guides)
diff --git a/ansible_collections/sensu/sensu_go/changelogs/changelog.yaml b/ansible_collections/sensu/sensu_go/changelogs/changelog.yaml
new file mode 100644
index 00000000..42569b99
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/changelogs/changelog.yaml
@@ -0,0 +1,456 @@
+---
+releases:
+ 1.13.2:
+ release_date: "2022-12-22"
+ changes:
+ release_summary: |
+ Support for latest Ansible and Rocky and Alma linux
+ minor_changes:
+ - Added support for ansible 2.14
+ - Added support for Rocky and Alma linux
+ 1.13.1:
+ release_date: "2022-04-19"
+ changes:
+ release_summary: |
+ Support for latest Ansible
+ minor_changes:
+ - Added support for ansible 2.13
+ - Removed support for CentOS 8
+ 1.13.0:
+ release_date: "2022-01-17"
+ changes:
+ release_summary: |
+ Bonsai asset definitions can be downloaded on controller or remote nodes
+
+ A user can decide if Bonsai asset definitions should be downloaded to
+ remote nodes or the controller node. This is useful, but not limited to,
+ in case that the controller node's Internet connection is unstable or in
+ general worse than that of the remote nodes.
+ minor_changes:
+ - Added argument remote_on inside bonsai_asset module
+
+ 1.12.1:
+ release_date: "2021-12-20"
+ changes:
+ release_summary: |
+ Keeping up with updates
+ minor_changes:
+ - Add Sensu Go 6.5.5 Windows metadata
+ - Add sensu Go 6.6.2 Windows metadata
+ 1.12.0:
+ release_date: "2021-08-31"
+ changes:
+ release_summary: |
+ Keeping up with the updates
+
+ In this release, community contributed support for the OracleLinux. We
+ added a few tests to catch if things break in the future and this is
+ about it. And while we were at work, we also added support for Sensu Go
+ 6.4.0 and 6.4.1 on Windows.
+ minor_changes:
+ - Add support for OracleLinux.
+ - Add Sensu Go 6.4.0 Windows metadata.
+ - Add Sensu Go 6.4.1 Windows metadata.
+
+ 1.11.1:
+ release_date: "2021-05-27"
+ changes:
+ release_summary: |
+ Sensu Go 6.3.0 is here
+
+ For this release, we only updated the list of available Sensu Go agent
+ versions for Windows, and made sure collection works with the latest
+ Sensu Go version.
+ minor_changes:
+ - Update list of available Sensu Go agent packages for Windows
+ installations (added 6.3.0).
+
+ 1.11.0:
+ release_date: "2021-05-18"
+ changes:
+ release_summary: |
+ Validate all the things!
+
+ If you ever thought to yourself, "Ansible does not yell enough at me,"
+ we have some great news. The Sensu Go Ansible Collection gained role
+ argument specifications, making it possible to validate variable values
+ before executing a role. You are welcome ;)
+ minor_changes:
+ - Add argument specification to the install role.
+ - Add argument specification to the backend role.
+ - Add argument specification to the agent role.
+
+ 1.10.0:
+ release_date: "2021-05-04"
+ changes:
+ release_summary: |
+ Authentication, authentication on the wall, Who has Access to Them All?
+
+ New modules allow Sensu Go users to configure authentication within
+ their Ansible playbooks. The users can authenticate via external
+ authentication providers such as Lightweight Directory Access Protocol (LDAP),
+ Active Directory (AD), or OpenID Connect 1.0 protocol (OIDC).
+ minor_changes:
+ - Add modules for managing Sensu Go authentication providers.
+ modules:
+ - name: ad_auth_provider
+ description: Manage Sensu AD authentication provider
+ namespace: ""
+ - name: ldap_auth_provider
+ description: Manage Sensu LDAP authentication provider
+ namespace: ""
+ - name: oidc_auth_provider
+ description: Manage Sensu OIDC authentication provider
+ namespace: ""
+ - name: auth_provider_info
+ description: List Sensu authentication providers
+ namespace: ""
+
+ 1.9.4:
+ release_date: "2021-03-30"
+ changes:
+ release_summary: |
+ Opening Windows for real
+
+ This is a bugfix release that makes sure Sensu Go Ansible Collection
+ can operate even in the absence of the Windows Ansible Collection
+ (assuming we do not want to manage agents on Windows hosts, that is).
+ bugfixes:
+ - Make sure we lazy-load Windows-related content.
+
+ 1.9.3:
+ release_date: "2021-03-30"
+ changes:
+ release_summary: |
+ Opening Windows
+
+ The only change in this release is removal of the ``ansible.windows``
+ dependency. This should allow users that only use certified collections
+ to install and use the collection.
+ minor_hanges:
+ - Remove Windows Ansible Collection dependency.
+
+ 1.9.2:
+ release_date: "2021-03-28"
+ changes:
+ release_summary: |
+ A fresh batch of updates
+
+ For this release, we only updated the list of available Sensu Go agent
+ versions for Windows.
+ minor_hanges:
+ - Update list of available Sensu Go agent packages for Windows
+ installations (added 5.21.4, 5.21.5, 6.2.5, and 6.2.6).
+ 1.9.1:
+ release_date: "2021-03-08"
+ changes:
+ release_summary: |
+ Containerize all the things
+
+ There are two main reasons for this release. We made sure the Sensu Go
+ Ansible Collection works with development version of Ansible (upcoming
+ ansible-core 2.11). And we added enough metadata to the collection that
+ ansible-builder can create an execution environment with the Sensu Go
+ Ansible Collection without having to manually specify dependencies.
+ bugfixes:
+ - Add ansible.windows dependency that we forgot to add when we
+ introducted the Sensu Go agent installation on Windows.
+
+ 1.9.0:
+ release_date: "2021-02-28"
+ changes:
+ release_summary: |
+ Multi-cluster visibility with federation
+
+ Two new module pairs allow Sensu Go users to configure federation from
+ the comfort of their Ansible playbooks.
+ minor_changes:
+ - Add modules for managing etcd replicatiors, which form the basis of
+ the Sensu Go federation.
+ - Add modules for managing Sensu Go clusters.
+ - Update list of available Sensu Go agent packages for Windows
+ installations.
+ bugfixes:
+ - Allow downgrading Sensu Go packages on Linux distributions that use
+ yum or dnf for package management.
+ modules:
+ - name: cluster
+ description: Manage Sensu Go clusters
+ namespace: ""
+ - name: cluster_info
+ description: List available Sensu Go clusters
+ namespace: ""
+ - name: etcd_replicator
+ description: Manage Sensu Go etcd replicators
+ namespace: ""
+ - name: etcd_replicator_info
+ description: List Sensu Go etcd replicators
+ namespace: ""
+
+ 1.8.0:
+ release_date: "2021-01-26"
+ changes:
+ release_summary: |
+ Supporting hashed user passwords
+
+ Starting with this release, Sensu Go users can use password hashes
+ directly when manipulating role-based access control resources.
+ minor_changes:
+ - Add support for hashed password in user module.
+ bugfixes:
+ - Mimic actual responses when user module runs in check mode.
+ - Make it possible to use modules on Sensu Go backends with no version
+ number.
+
+ 1.7.2:
+ release_date: "2021-01-21"
+ changes:
+ release_summary: |
+ Be kind
+
+ The main thing in this release is a small adjustment of our code of
+ conduct that is a bit more generic and less event-focused.
+ minor_changes:
+ - Specify minimal python version for modules.
+ - Update code of conduct.
+ - List version 6.2.1 and 6.2.2 in Windows lookup table.
+
+ 1.7.1:
+ release_date: "2020-12-30"
+ changes:
+ release_summary: |
+ Slow and steady
+
+ There are no major new features in this release, just honest little
+ fixes that should make using Sensu Go Ansible Collection a bit more
+ pleasant.
+ minor_changes:
+ - List version 6.2.0 and 6.1.3 in Windows lookup table.
+ - Add module return value samples.
+
+ 1.7.0:
+ release_date: "2020-12-14"
+ changes:
+ release_summary: |
+ Say hello to Amazon Linux and Windows
+
+ As the title suggests, we worked hard to bring you two new supported
+ platforms to the Sensu Go Ansible Colletions. And yes, all your
+ existing playbooks still work.All you need to do is run them against
+ the right host and voila ;)
+ minor_changes:
+ - Add support for installing Sensu Go on Amazon Linux.
+ - Add support for installing Sensu Go agents on Windows.
+
+ 1.6.1:
+ release_date: "2020-11-04"
+ changes:
+ release_summary: |
+ Comparing entities is hard
+
+ This is a bugfix release that makes sure agent entity changes are
+ properly detected.
+ bugfixes:
+ - Make subscriptions comparison insensitive to ordering.
+ - Make sure agent entities handle *entity:{name}* automatic
+ subscriptions.
+
+ 1.6.0:
+ release_date: "2020-10-12"
+ changes:
+ release_summary: |
+ Our little secret
+
+ This release contains a few new modules that allow you to manage all
+ things related to the Sensu Go secrets: from adding secrets providers
+ to passing secrets to resources that know how to use them.
+ minor_changes:
+ - Add modules for managing Sensu Go secret providers.
+ - Add modules for managing Sensu Go secrets.
+ - Add support for secrets to pipe handler module.
+ - Add support for secrets to check module.
+ - Add support for secrets to mutator module.
+ modules:
+ - name: secret
+ description: Manage Sensu Go secrets
+ namespace: ""
+ - name: secret_info
+ description: List available Sensu Go secrets
+ namespace: ""
+ - name: secrets_provider_env
+ description: Manage Sensu Env secrets provider
+ namespace: ""
+ - name: secrets_provider_vault
+ description: Manage Sensu VaultProvider secrets provider
+ namespace: ""
+ - name: secrets_provider_info
+ description: List Sensu secrets providers
+ namespace: ""
+
+ 1.5.0:
+ release_date: "2020-07-24"
+ changes:
+ release_summary: |
+ Self-signed security
+
+ The primary focus of this release is to enable configuration of Sensu
+ Go backends that use certificates that are not considered trusted when
+ using system-provided CA bundle.
+ minor_changes:
+ - Allow modules to supply custom CA bundle for backend certificate
+ validation or skip the validation entirely.
+ bugfixes:
+ - Expand documentation about the *check_hooks* parameter in the check
+ module.
+ - Explain how the resource name parameter is used and what invariants
+ need to hold in order for the Sensu Go to consider it a valid name.
+
+ 1.4.2:
+ release_date: "2020-07-02"
+ changes:
+ release_summary: |
+ Break the fall
+
+ There is really only one reason for this release: making sure user
+ management works with Sensu Go 5.21.0 and newer. And while the
+ upstream did break the API, we did not, so all your playbooks should
+ function as nothing happened. We had to add a *bcrypt* dependency to
+ our collection so make sure it is installed on hosts that will execute
+ the user module.
+ bugfixes:
+ - Make sure check module is as idempotent as possible.
+ - Make user module compatible with Sensu Go >= 5.21.0.
+
+ 1.4.1:
+ release_date: "2020-06-25"
+ changes:
+ release_summary: |
+ Maintenance is the name of the game
+
+ There are no nothing earth-shattering changes in this release, just
+ honest little bug fixes and compatibility improvements.
+
+ **NOTE:** The *sensu.sensu_go.user* module currently **DOES NOT** work
+ on Sensu Go 5.21.0 and later. This is a know issue that will be fixed
+ as soon as the updated user-related backend API endpoints are
+ documented.
+ bugfixes:
+ - Make sure event module always returns a predicted result.
+ - Make user module fully-idempotent. Previous versions did not
+ properly detect the password changes.
+ - Use fully-qualified collection names in module documentation.
+ - Ensure backend initialization properly reports changed state.
+ - Make API key authentication work even for regular users with limited
+ permissions.
+ - Update the datastore module to cope with the minor API changes.
+
+ 1.4.0:
+ release_date: "2020-04-30"
+ changes:
+ release_summary: |
+ Keeping up with the world
+
+ Main changes in this release are related to updates in the Sensu Go's
+ web API that broke our change detection.
+ minor_changes:
+ - Add support for RHEL and CentOS 8.
+ bugfixes:
+ - Fix resource metadata comparison on Sensu Go 5.19.0 and newer.
+ - Update entity comparator to handle new fields.
+
+ 1.3.1:
+ release_date: "2020-03-21"
+ changes:
+ release_summary: |
+ Bug fixing galore
+
+ This release makes it possible to use the *asset* module when
+ replacing the deprecated, single-build assets that were created by
+ means other than Ansible.
+ bugfixes:
+ - Do not die when encountering a deprecated asset format.
+ - Update return value documentation for info modules.
+ - Add Sensu Go 5.17.x and 5.18.x to the test suite and remove the
+ unsupported versions (5.14.2 and lower).
+ - Update the role metadata with proper platform markers.
+ - Remove unsupported Ubuntu versions from the test suite.
+
+ 1.3.0:
+ release_date: "2020-02-03"
+ changes:
+ release_summary: |
+ Authenticating with style on Debian
+
+ Sensu Go 5.15.0 gained an API key authentication method and the
+ Ansible collection finally caught up. This means that we can now
+ replace *user* and *password* authentication parameters with a single
+ *api_key* value.
+
+ And the other big news is the addition of Debian support to the
+ `install` role.
+ minor_changes:
+ - Add API key authentication support.
+ - Add support for Debian installation.
+
+ 1.2.0:
+ release_date: "2020-01-17"
+ changes:
+ release_summary: |
+ Building support for builds
+
+ This release adds support for specifying builds when installing
+ various Sensu Go components.
+ minor_changes:
+ - Add *build* variable to the *install* role that further pins down
+ the package version that gets installed.
+
+ 1.1.1:
+ release_date: "2020-01-08"
+ changes:
+ release_summary: |
+ Python 2 is Still a Thing
+
+ This is a bugfix release that makes sure the Sensu collection is
+ working when Ansible control node uses Python 2.
+ minor_changes:
+ - Add support for RHEL 7 to the install role (thanks, @danragnar).
+ bugfixes:
+ - Accept *str* and *unicode* instance as a valid string in
+ *bonsai_asset* action plugin.
+
+ 1.1.0:
+ release_date: "2019-12-28"
+ changes:
+ release_summary: |
+ Hello Sensu Go 5.16
+
+ This is the first release that supports installing Sensu Go 5.16.
+ minor_changes:
+ - Support for Sensu Go 5.16 initialization in backend role.
+ - Support for external datastore management using *datastore* and
+ *datastore_info* modules.
+ bugfixes:
+ - Reintroduce namespace support to *bonsai_asset* module (thanks,
+ @jakeo)
+ modules:
+ - name: datastore
+ description: Manage Sensu external datastore providers
+ namespace: ""
+ - name: datastore_info
+ description: List external Sensu datastore providers
+ namespace: ""
+
+ 1.0.0:
+ release_date: "2019-12-09"
+ changes:
+ release_summary: |
+ Rising From The Ashes
+
+ This is the initial stable release of the Sensu Go Ansible Collection.
+ It contains roles for installing and configuring Sensu Go backends and
+ agents and a set of modules for managing Sensu Go resources.
+
+ Where does the release name comes from? We took an existing Ansible
+ Collection that @flowerysong wrote, gave it a thorough tune-up and
+ added a comprehensive test suite. And now, it is ready to face the
+ world!
diff --git a/ansible_collections/sensu/sensu_go/collection.requirements b/ansible_collections/sensu/sensu_go/collection.requirements
new file mode 100644
index 00000000..7f0b6e75
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/collection.requirements
@@ -0,0 +1 @@
+bcrypt
diff --git a/ansible_collections/sensu/sensu_go/docker/alma-8.docker b/ansible_collections/sensu/sensu_go/docker/alma-8.docker
new file mode 100644
index 00000000..88326b2f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/alma-8.docker
@@ -0,0 +1,6 @@
+FROM almalinux:8
+RUN dnf makecache \
+ && dnf install -y \
+ /usr/bin/python3 /usr/bin/python3-config /usr/bin/dnf-3 \
+ sudo bash iproute \
+ && dnf clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/amazon-1.docker b/ansible_collections/sensu/sensu_go/docker/amazon-1.docker
new file mode 100644
index 00000000..f6316c35
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/amazon-1.docker
@@ -0,0 +1,9 @@
+FROM amazonlinux:1
+ENV container docker
+RUN \
+ yum makecache fast; \
+ yum install -y \
+ /usr/bin/python /usr/bin/python2-config sudo \
+ yum-plugin-ovl bash iproute shadow-utils; \
+ sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf; \
+ yum clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/amazon-2.docker b/ansible_collections/sensu/sensu_go/docker/amazon-2.docker
new file mode 100644
index 00000000..734c3c95
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/amazon-2.docker
@@ -0,0 +1,9 @@
+FROM amazonlinux:2
+ENV container docker
+RUN \
+ yum makecache fast; \
+ yum install -y \
+ /usr/bin/python /usr/bin/python2-config sudo \
+ yum-plugin-ovl bash iproute shadow-utils; \
+ sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf; \
+ yum clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/build-all.sh b/ansible_collections/sensu/sensu_go/docker/build-all.sh
new file mode 100755
index 00000000..dae03194
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/build-all.sh
@@ -0,0 +1,8 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+for f in *.docker
+do
+ ./build.sh "$f"
+done
diff --git a/ansible_collections/sensu/sensu_go/docker/build.sh b/ansible_collections/sensu/sensu_go/docker/build.sh
new file mode 100755
index 00000000..a6c56fcb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/build.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+readonly filename="$1"; shift
+
+readonly base=${filename%.docker}
+readonly name=${base%-*}
+readonly version=${base##*-}
+readonly tag="quay.io/xlab-steampunk/sensu-go-tests-$name:$version"
+
+docker build --pull -f "$filename" -t "$tag" .
+docker push "$tag"
diff --git a/ansible_collections/sensu/sensu_go/docker/centos-6.docker b/ansible_collections/sensu/sensu_go/docker/centos-6.docker
new file mode 100644
index 00000000..1e7ec0e4
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/centos-6.docker
@@ -0,0 +1,7 @@
+FROM centos:6
+RUN yum makecache fast \
+ && yum install -y \
+ /usr/bin/python /usr/bin/python2-config sudo \
+ yum-plugin-ovl bash iproute \
+ && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf \
+ && yum clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/centos-7.docker b/ansible_collections/sensu/sensu_go/docker/centos-7.docker
new file mode 100644
index 00000000..c11f9b55
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/centos-7.docker
@@ -0,0 +1,25 @@
+FROM centos:7
+ENV container docker
+RUN ( \
+ cd /lib/systemd/system/sysinit.target.wants/; \
+ for i in *; do \
+ [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; \
+ done \
+ ); \
+ rm -f /lib/systemd/system/multi-user.target.wants/*;\
+ rm -f /etc/systemd/system/*.wants/*;\
+ rm -f /lib/systemd/system/local-fs.target.wants/*; \
+ rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
+ rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
+ rm -f /lib/systemd/system/basic.target.wants/*;\
+ rm -f /lib/systemd/system/anaconda.target.wants/*; \
+ yum makecache fast; \
+ yum install -y \
+ /usr/bin/python /usr/bin/python2-config sudo \
+ yum-plugin-ovl bash iproute; \
+ sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf; \
+ yum clean all; \
+ chmod 777 /root;
+
+VOLUME [ "/sys/fs/cgroup" ]
+CMD [ "/usr/sbin/init" ]
diff --git a/ansible_collections/sensu/sensu_go/docker/centos-8.docker b/ansible_collections/sensu/sensu_go/docker/centos-8.docker
new file mode 100644
index 00000000..11ceae57
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/centos-8.docker
@@ -0,0 +1,6 @@
+FROM centos:8
+RUN dnf makecache \
+ && dnf install -y \
+ /usr/bin/python3 /usr/bin/python3-config /usr/bin/dnf-3 \
+ sudo bash iproute \
+ && dnf clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/debian-10.docker b/ansible_collections/sensu/sensu_go/docker/debian-10.docker
new file mode 100644
index 00000000..c53007c0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/debian-10.docker
@@ -0,0 +1,8 @@
+FROM debian:10
+RUN apt-get update \
+ && apt-get install -y \
+ python sudo bash ca-certificates iproute2 python-apt aptitude \
+ && rm -rf /var/lib/apt/lists/* \
+ && rm -rf /usr/share/doc \
+ && rm -rf /usr/share/man \
+ && apt-get clean
diff --git a/ansible_collections/sensu/sensu_go/docker/debian-9.docker b/ansible_collections/sensu/sensu_go/docker/debian-9.docker
new file mode 100644
index 00000000..a0013d77
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/debian-9.docker
@@ -0,0 +1,5 @@
+FROM debian:9
+RUN apt-get update \
+ && apt-get install -y \
+ python sudo bash ca-certificates iproute2 python-apt aptitude \
+ && apt-get clean
diff --git a/ansible_collections/sensu/sensu_go/docker/oracle-8.docker b/ansible_collections/sensu/sensu_go/docker/oracle-8.docker
new file mode 100644
index 00000000..c63a2bf7
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/oracle-8.docker
@@ -0,0 +1,6 @@
+FROM oraclelinux:8
+RUN dnf makecache \
+ && dnf install -y \
+ /usr/bin/python3 /usr/bin/python3-config /usr/bin/dnf-3 \
+ sudo bash iproute \
+ && dnf clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/redhat-7.docker b/ansible_collections/sensu/sensu_go/docker/redhat-7.docker
new file mode 100644
index 00000000..f48b1701
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/redhat-7.docker
@@ -0,0 +1,7 @@
+FROM registry.access.redhat.com/ubi7/ubi-init:latest
+RUN yum makecache fast \
+ && yum install -y \
+ /usr/bin/python /usr/bin/python2-config sudo \
+ yum-plugin-ovl bash iproute \
+ && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf \
+ && yum clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/rocky-8.docker b/ansible_collections/sensu/sensu_go/docker/rocky-8.docker
new file mode 100644
index 00000000..b9e149a8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/rocky-8.docker
@@ -0,0 +1,6 @@
+FROM rockylinux:8
+RUN dnf makecache \
+ && dnf install -y \
+ /usr/bin/python3 /usr/bin/python3-config /usr/bin/dnf-3 \
+ sudo bash iproute \
+ && dnf clean all
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-5.14.2.docker b/ansible_collections/sensu/sensu_go/docker/sensu-5.14.2.docker
new file mode 100644
index 00000000..ec833991
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-5.14.2.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:5.14.2
+RUN apk update \
+ && apk add --no-cache python sudo bash ca-certificates
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-5.21.3.docker b/ansible_collections/sensu/sensu_go/docker/sensu-5.21.3.docker
new file mode 100644
index 00000000..023f33c8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-5.21.3.docker
@@ -0,0 +1,9 @@
+FROM sensu/sensu:5.21.3
+RUN apk update \
+ && apk add --no-cache python sudo bash ca-certificates \
+ py-bcrypt py-six py-cffi
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.2.5.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.2.5.docker
new file mode 100644
index 00000000..9fd19072
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.2.5.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.2.5
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.3.0.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.3.0.docker
new file mode 100644
index 00000000..12399d3b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.3.0.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.3.0
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.4.3.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.4.3.docker
new file mode 100644
index 00000000..7afcd120
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.4.3.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.4.3
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.5.5.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.5.5.docker
new file mode 100644
index 00000000..1ceea051
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.5.5.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.5.5
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.6.2.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.6.2.docker
new file mode 100644
index 00000000..613e7c28
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.6.2.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.6.2
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.7.5.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.7.5.docker
new file mode 100644
index 00000000..058ea335
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.7.5.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.7.5
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.8.2.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.8.2.docker
new file mode 100644
index 00000000..12228196
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.8.2.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.8.2
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/sensu-6.9.0.docker b/ansible_collections/sensu/sensu_go/docker/sensu-6.9.0.docker
new file mode 100644
index 00000000..c0d99556
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/sensu-6.9.0.docker
@@ -0,0 +1,8 @@
+FROM sensu/sensu:6.9.0
+RUN apk update \
+ && apk add --no-cache python3 py3-bcrypt py3-six py3-cffi bash
+CMD [ \
+ "sensu-backend", "start", \
+ "--state-dir", "/var/lib/sensu/sensu-backend", \
+ "--log-level", "debug" \
+]
diff --git a/ansible_collections/sensu/sensu_go/docker/ubuntu-14.04.docker b/ansible_collections/sensu/sensu_go/docker/ubuntu-14.04.docker
new file mode 100644
index 00000000..720f50d4
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/ubuntu-14.04.docker
@@ -0,0 +1,8 @@
+FROM ubuntu:14.04
+RUN rm \
+ /etc/apt/apt.conf.d/20apt-esm-hook.conf \
+ /etc/apt/sources.list.d/ubuntu-esm-infra-trusty.list \
+ && apt-get update \
+ && apt-get install -y \
+ python sudo bash ca-certificates iproute2 python-apt aptitude \
+ && apt-get clean
diff --git a/ansible_collections/sensu/sensu_go/docker/ubuntu-16.04.docker b/ansible_collections/sensu/sensu_go/docker/ubuntu-16.04.docker
new file mode 100644
index 00000000..37a96a02
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/ubuntu-16.04.docker
@@ -0,0 +1,5 @@
+FROM ubuntu:16.04
+RUN apt-get update \
+ && apt-get install -y \
+ python sudo bash ca-certificates iproute2 python-apt aptitude \
+ && apt-get clean
diff --git a/ansible_collections/sensu/sensu_go/docker/ubuntu-18.04.docker b/ansible_collections/sensu/sensu_go/docker/ubuntu-18.04.docker
new file mode 100644
index 00000000..7d6230af
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docker/ubuntu-18.04.docker
@@ -0,0 +1,5 @@
+FROM ubuntu:18.04
+RUN apt-get update \
+ && apt-get install -y \
+ python sudo bash ca-certificates iproute2 python-apt aptitude \
+ && apt-get clean
diff --git a/ansible_collections/sensu/sensu_go/docs.requirements b/ansible_collections/sensu/sensu_go/docs.requirements
new file mode 100644
index 00000000..a28b3763
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs.requirements
@@ -0,0 +1,3 @@
+Sphinx
+sphinx-rtd-theme
+ansible-doc-extractor
diff --git a/ansible_collections/sensu/sensu_go/docs/Makefile b/ansible_collections/sensu/sensu_go/docs/Makefile
new file mode 100644
index 00000000..d0c3cbf1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/ansible_collections/sensu/sensu_go/docs/Makefile.custom b/ansible_collections/sensu/sensu_go/docs/Makefile.custom
new file mode 100644
index 00000000..b896ab9c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/Makefile.custom
@@ -0,0 +1,19 @@
+modules := $(wildcard ../plugins/modules/*.py)
+module_docs := $(modules:../plugins/modules/%.py=source/modules/%.rst)
+doc_fragments := $(wildcard ../plugins/doc_fragments/*.py)
+template := templates/module.rst.j2
+
+export ANSIBLE_COLLECTIONS_PATHS ?= $(realpath $(CURDIR)/../../../..)
+
+
+.PHONY: all
+docs: $(module_docs)
+ $(MAKE) html
+
+.PHONY: clean
+clean:
+ rm -rf build source/modules
+
+source/modules/%.rst: ../plugins/modules/%.py $(doc_fragments) $(template)
+ mkdir -p source/modules
+ ansible-doc-extractor --template $(template) source/modules $<
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/installation/ansible.cfg b/ansible_collections/sensu/sensu_go/docs/examples/installation/ansible.cfg
new file mode 100644
index 00000000..72427f5b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/installation/ansible.cfg
@@ -0,0 +1,10 @@
+[galaxy]
+server_list = automation_hub, galaxy
+
+[galaxy_server.automation_hub]
+url=https://cloud.redhat.com/api/automation-hub/
+auth_url=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token
+token=AABBccddeeff112233gghh...
+
+[galaxy_server.galaxy]
+url=https://galaxy.ansible.com/
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/quickstart/inventory.yaml b/ansible_collections/sensu/sensu_go/docs/examples/quickstart/inventory.yaml
new file mode 100644
index 00000000..baa986e7
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/quickstart/inventory.yaml
@@ -0,0 +1,9 @@
+all:
+ children:
+ backends:
+ hosts:
+ 192.168.50.4:
+
+ agents:
+ hosts:
+ 192.168.50.5:
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-5.yaml b/ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-5.yaml
new file mode 100644
index 00000000..eb89c7b5
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-5.yaml
@@ -0,0 +1,51 @@
+---
+- name: Install, configure and run Sensu backend
+ hosts: backends
+ become: true
+
+ tasks:
+ - name: Install backend
+ include_role:
+ name: sensu.sensu_go.backend
+ vars:
+ version: 5.21.2
+
+- name: Install, configure and run Sensu agents
+ hosts: agents
+ become: true
+
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ version: 5.21.2
+ agent_config:
+ deregister: true
+ keepalive-interval: 5
+ keepalive-timeout: 10
+ subscriptions:
+ - linux
+
+- name: Configure your first monitor
+ hosts: localhost
+ tasks:
+ - name: Create sensu asset
+ sensu.sensu_go.bonsai_asset:
+ auth: &auth
+ url: http://{{ groups['backends'][0] }}:8080
+ name: sensu/monitoring-plugins
+ version: 2.2.0-1
+
+ - name: Create sensu ntp check
+ sensu.sensu_go.check:
+ auth: *auth
+ name: ntp
+ runtime_assets: sensu/monitoring-plugins
+ command: check_ntp_time -H time.nist.gov --warn 0.5 --critical 1.0
+ output_metric_format: nagios_perfdata
+ publish: true
+ interval: 30
+ timeout: 10
+ subscriptions:
+ - linux
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-6.yaml b/ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-6.yaml
new file mode 100644
index 00000000..184ce5b7
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/quickstart/playbook-6.yaml
@@ -0,0 +1,58 @@
+---
+- name: Install, configure and run Sensu backend
+ hosts: backends
+ become: true
+
+ tasks:
+ - name: Install backend
+ include_role:
+ name: sensu.sensu_go.backend
+ vars:
+ version: 6.0.0
+
+- name: Install, configure and run Sensu agents
+ hosts: agents
+ become: true
+
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ version: 6.0.0
+ agent_config:
+ name: my-agent
+ keepalive-interval: 5
+ keepalive-timeout: 10
+
+- name: Configure your first monitor
+ hosts: localhost
+ tasks:
+ - name: Add subscriptions to agent entity
+ sensu.sensu_go.entity:
+ auth: &auth
+ url: http://{{ groups['backends'][0] }}:8080
+ name: my-agent
+ entity_class: agent
+ deregister: true
+ subscriptions:
+ - linux
+
+ - name: Create sensu asset
+ sensu.sensu_go.bonsai_asset:
+ auth: *auth
+ name: sensu/monitoring-plugins
+ version: 2.2.0-1
+
+ - name: Create sensu ntp check
+ sensu.sensu_go.check:
+ auth: *auth
+ name: ntp
+ runtime_assets: sensu/monitoring-plugins
+ command: check_ntp_time -H time.nist.gov --warn 0.5 --critical 1.0
+ output_metric_format: nagios_perfdata
+ publish: true
+ interval: 30
+ timeout: 10
+ subscriptions:
+ - linux
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/roles/agent.yaml b/ansible_collections/sensu/sensu_go/docs/examples/roles/agent.yaml
new file mode 100644
index 00000000..f90ac934
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/roles/agent.yaml
@@ -0,0 +1,8 @@
+---
+- name: Install, configure and run Sensu agent
+ hosts: agents
+ roles:
+ - sensu.sensu_go.agent
+ vars:
+ agent_config:
+ backend-url: [ "ws://upstream-backend:4321" ]
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/roles/backend.yaml b/ansible_collections/sensu/sensu_go/docs/examples/roles/backend.yaml
new file mode 100644
index 00000000..9573a378
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/roles/backend.yaml
@@ -0,0 +1,7 @@
+- name: Install, configure and run Sensu backend
+ hosts: backends
+ roles:
+ - sensu.sensu_go.backend
+ vars:
+ backend_config:
+ log-level: debug
diff --git a/ansible_collections/sensu/sensu_go/docs/examples/roles/install.yaml b/ansible_collections/sensu/sensu_go/docs/examples/roles/install.yaml
new file mode 100644
index 00000000..4e028236
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/examples/roles/install.yaml
@@ -0,0 +1,23 @@
+---
+- name: Install latest sensu-backend binary from stable channel
+ hosts: backends
+ roles:
+ - sensu.sensu_go.install
+ vars:
+ components: [ sensu-go-backend ]
+
+- name: Install latest sensu-agent binary from testing channel
+ hosts: agents
+ roles:
+ - sensu.sensu_go.install
+ vars:
+ components: [ sensu-go-agent ]
+ channel: testing
+
+- name: Install a specific version of sensuctl binary from stable channel
+ hosts: localhost
+ roles:
+ - sensu.sensu_go.install
+ vars:
+ components: [ sensu-go-cli ]
+ version: 5.14.2
diff --git a/ansible_collections/sensu/sensu_go/docs/source/conf.py b/ansible_collections/sensu/sensu_go/docs/source/conf.py
new file mode 100644
index 00000000..46c1505f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/conf.py
@@ -0,0 +1,17 @@
+project = "Sensu Go Ansible Collection"
+copyright = "2019, XLAB Steampunk"
+author = "XLAB Steampunk"
+
+extensions = [
+ "sphinx_rtd_theme",
+]
+exclude_patterns = []
+
+html_theme = "sphinx_rtd_theme"
+html_context = {
+ "display_github": True,
+ "github_user": "sensu",
+ "github_repo": "sensu-go-ansible",
+ "github_version": "master",
+ "conf_py_path": "/docs/source/",
+}
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking.rst
new file mode 100644
index 00000000..8192945c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking.rst
@@ -0,0 +1,17 @@
+Developing Sensu Go Ansible Collection
+======================================
+
+So, you have decided to help us out. Great! Let us set up a development
+environment together, and then we can start hacking ;)
+
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Content
+
+ hacking/setup
+ hacking/testing
+ hacking/docker-images
+ hacking/documentation
+ hacking/windows
+ hacking/releases
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking/docker-images.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking/docker-images.rst
new file mode 100644
index 00000000..6196dba0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking/docker-images.rst
@@ -0,0 +1,19 @@
+Preparing docker images for integration tests
+=============================================
+
+Our test suite relies heavily on custom docker images for testing. The main
+reason that we use custom images is test speed: having prepared docker images
+vs. building them on each test execution saves a ton of time.
+
+Adding a new image is relatively straightforward process. We can just copy one
+of the preexisting definitions from the *docker* directory and update it. All
+docker files should have a name in the format of ``<name>-<tag>.docker``.
+
+To build and publish our image, we can run the *docker/build.sh* script::
+
+ $ cd docker
+ $ ./build.sh sensu-5.21.0.docker
+
+Note that the command will add a *sensu-go-tests-* prefix to all images. In
+the previous example, the build script will build the
+*quay.io/xlab-steampunk/sensu-go-tests-sensu:5.21.0* image.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking/documentation.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking/documentation.rst
new file mode 100644
index 00000000..7830b10e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking/documentation.rst
@@ -0,0 +1,74 @@
+Documenting Sensu Go Ansible collection
+=======================================
+
+Documentation for the Sensu Go Ansible collection consists of two distinct
+kinds: the general guides and the API documentation. The general guides live
+in the ``docs/source`` directory while the API documentation is part of the
+modules' source code.
+
+
+Authoring the documentation
+---------------------------
+
+Adding a new piece of general documentation is reasonably straightforward. We
+need to create a new file somewhere inside the ``docs/source`` directory and
+fill it with reStructuredText-formatted content.
+
+As a general rule, we use two levels of headings: a document title and as many
+section titles as needed. If you feel you need something more complex, try
+simplifying the documentation first. The end-users will thank you for this.
+
+First few lines of the document you are just reading look like this:
+
+.. literalinclude:: documentation.rst
+ :language: rst
+ :lines: 1-20
+
+Once we have the content ready, we need to bind it with the rest of the
+documentation. We do this by adding our document to one of the ``toctree``
+directives. We can find the important ones in the ``index.rst`` file.
+
+We follow the `upstream guides`_ when it comes to documenting the modules,
+with one exception: ``version_added`` field holds the Sensu Go collection
+version instead of Ansible version. Typical module starts with the next few
+lines:
+
+.. literalinclude:: ../../../plugins/modules/handler_set.py
+ :language: python
+ :lines: 1-30
+
+.. _upstream guides:
+ https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_documenting.html
+
+
+Publishing the documentation
+----------------------------
+
+We use the `ansible-doc-extractor`_ tool to convert the embedded module
+documentation into a set of reStructuredText documents. Once we have those
+documents ready, we bind the various parts of the documentation together using
+Sphinx_. In practice, we just run the ``make docs`` command in the root of the
+repository and open the ``docs/build/html/index.html`` file in our browser of
+choice.
+
+.. _ansible-doc-extractor: https://github.com/xlab-si/ansible-doc-extractor
+.. _Sphinx: https://www.sphinx-doc.org
+
+We use `GitHub Pages`_ for hosting the online version of our documentation. To
+update the content of our `documentation site`_, we must copy the rendered
+documentation into the ``gh-pages`` branch, commit the changes, and push them
+to GitHub. The described procedure translates into the following series of
+steps::
+
+ (venv) $ make docs
+ (venv) $ git worktree add gh-pages gh-pages
+ (venv) $ rm -rf gh-pages/*
+ (venv) $ cp -r docs/build/html/* gh-pages
+ (venv) $ cd gh-pages
+ (venv) $ git add . && git commit
+ (venv) $ git push origin gh-pages
+ (venv) $ cd ..
+ (venv) $ git worktree remove gh-pages
+
+.. _GitHub Pages: https://pages.github.com/
+.. _documentation site: https://sensu.github.io/sensu-go-ansible/
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking/releases.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking/releases.rst
new file mode 100644
index 00000000..edf993af
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking/releases.rst
@@ -0,0 +1,49 @@
+Releasing the Sensu Go Ansible Collection
+=========================================
+
+The Sensu Go Ansible collection is primarily available from `Ansible Galaxy`_
+and `Automation Hub`_. Which means that we need to get our content up there
+somehow. But before we can start uploading things, we need to do some chores
+first.
+
+.. _Ansible Galaxy:
+ https://galaxy.ansible.com/sensu/sensu_go
+
+.. _Automation Hub:
+ https://cloud.redhat.com/ansible/automation-hub/sensu/sensu_go
+
+
+First, we need to tag the commit and move the ``stable`` branch forward::
+
+ $ VERSION=$(grep version: galaxy.yml | cut -d" " -f2)
+ $ git tag -am "Version $VERSION" v$VERSION
+ $ git branch -f stable v$VERSION
+
+Now, we need to package the collection. Because the ``ansible-galaxy
+collection build`` command will package anything that it can find next to the
+``galaxy.yml`` file, we need to execute it in a clean environment. This is why
+we will temporarily check out the *stable* branch into the *release*
+subdirectory, build the collection package, and then delete the checkout. This
+translates into the following sequence of commands::
+
+ $ git worktree add release stable
+ $ cd release
+ $ ansible-galaxy collection build
+ $ mv sensu-sensu_go-$VERSION.tar.gz ..
+ $ cd ..
+ $ git worktree remove release
+
+Now we can upload the package to Ansible Galaxy::
+
+ $ API_KEY=api-key-from-https://galaxy.ansible.com/me/preferences
+ $ ansible-galaxy collection publish \
+ --api-key "$API_KEY" \
+ sensu-sensu_go-$VERSION.tar.gz
+
+Last thing we need to do is push the ``stable`` branch and created tag to the
+GitHub and attach the package to the GitHub release::
+
+ $ git push origin stable v$VERSION
+
+We need to attach the asset manually at the moment. Fully automated solution
+is in the works.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking/setup.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking/setup.rst
new file mode 100644
index 00000000..6bbd02dc
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking/setup.rst
@@ -0,0 +1,94 @@
+Preparing a development environment
+===================================
+
+In this guide, we will go through the process of setting up a development
+environment.
+
+
+Prerequisites
+-------------
+
+There are a few things we will need to have installed on our workstation. They
+are, in no particular order:
+
+ * a python 3 interpreter with virtual environment support,
+ * ``git`` tools,
+ * a functioning docker installation (for integration tests), and
+ * SELinux python bindings (only on systems with SELinux installed).
+
+It should be relatively straightforward to get those things installed on our
+machine. Once we have them ready, we can start customizing our environment for
+Ansible development.
+
+
+Obtaining the source code
+-------------------------
+
+Sensu Go Ansible collection lives on GitHub, which means that we are just one
+command away from the full source code. But before we can clone the
+repository, we need to create a few directories that will hold the source
+code::
+
+ $ mkdir -p ansible_collections/sensu
+ $ cd ansible_collections/sensu
+ $ git clone git@github.com:sensu/sensu-go-ansible.git sensu_go
+ $ cd sensu_go
+
+.. note::
+
+ It is vitally important that we
+
+ 1. create two parent directories before checking out the code, and
+ 2. that we clone the code into the `sensu_go` directory.
+
+ Ansible development tools often assume that we are working from the
+ ``ansible_collections/<namespace>/<collection>`` directory and complain
+ loudly if they do not have it their way.
+
+And this is it.
+
+
+Installing development tools
+----------------------------
+
+The first thing we need to do is create a new virtual environment and activate
+it. ::
+
+ $ python3 -m venv ../../../venv
+ $ . ../../../venv/bin/activate
+
+.. note::
+
+ We intentionally created our virtual environment outside the collection
+ directory. This is a simple way of making sure that various static analysis
+ tools only report issues with our code and not with any of the
+ dependencies.
+
+Now we need to install Ansible. Since we are just starting, latest stable
+version will do just fine::
+
+ (venv) $ pip install ansible
+
+All that separates us now from the fully functioning development environment
+are a few dependencies. We can install them by running the next command::
+
+ (venv) $ make requirements
+
+There is just one more thing left for us to do: test the setup.
+
+
+Testing the setup
+-----------------
+
+To validate our setup, we can run the bundled tests and render the
+documentation::
+
+ (venv) $ make sanity
+ (venv) $ make units
+ (venv) $ make docs
+ (venv) $ make -j4 integration
+
+We may want to go grab a cup of tea after running that last command, since it
+will take a while (about fifteen minutes on a semi-decent development
+machine). And if all of the commands finished with no errors, we are ready to
+start developing.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking/testing.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking/testing.rst
new file mode 100644
index 00000000..217749b9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking/testing.rst
@@ -0,0 +1,125 @@
+Testing Sensu Go Ansible Collection
+===================================
+
+Sensu Go Ansible collection contains a comprehensive test suite. We use this
+test suite to make sure collection's source code
+
+1. meets the Ansible code quality standards,
+2. works with supported versions of Ansible,
+3. works with supported versions of Sensu Go, and
+4. works with supported versions of Python.
+
+
+Static analysis
+---------------
+
+We use a wide variety of linters and static code analysis tools to ensure our
+code base stays healthy. To run all checks, we can execute the following
+command::
+
+ (venv) $ make sanity
+
+This command will take care of installing requirements and running them.
+
+The tools we use are:
+
+ 1. ``flake8`` for making sure our python code is nice and tidy.
+ 2. ``ansible-lint`` to make sure our Ansible roles follow the best practices.
+ 3. ``ansible-test sanity`` for ensuring our modules and plugins will work on a
+ wide varienty of platforms.
+ 4. A custom script that makes sure roles metadata is in sync with Ansible
+ Galaxy.
+
+
+Unit tests
+----------
+
+We use unit tests to test the logic and implementation of our helper functions
+and various Ansible plugins that are part of the Sensu Go collection.
+
+Unit tests live in the ``tests/unit`` directory and mirror the structure of
+the code. For example, unit tests for the code in the
+``plugins/modules/asset.py`` are stored in the
+``tests/unit/plugins/modules/test_asset.py`` file. When we add new code to the
+collection, we must also include the tests for this piece of code in the same
+git commit.
+
+We group our tests using classes. Each function or method gets its own
+dedicated test class that contains one or more tests that check different
+aspects of the code under scrutiny.
+
+We use ``ansible-test units`` to execute the unit tests. But we can also
+execute::
+
+ (venv) $ make units
+
+to run them. This convenience ``make`` rule will all of our unit tests in a
+dedicated docker container againts all Python versions that Ansible supports on
+managed nodes. Additionally, it will also generate a code coverage report and
+place it in the ``tests/output/reports/coverage/index.html`` HTML file.
+
+Our CI process runs unit tests on each push to GitHub. And while developers
+usually run the tests only against one version of Ansible, CI tools run them
+against all supported versions, making sure our code is ready to be used on a
+variety of systems.
+
+
+Integration tests
+-----------------
+
+Integration tests are basically a collection of Ansible playbooks that we run
+against all supported Sensu Go versions and operating systems. Integration
+tests live in the ``tests/integration/molecule`` directory. We use Molecule_
+to manage our integration tests.
+
+.. _Molecule: https://molecule.readthedocs.io/en/stable/
+
+We can add a new test scenario by creating a suitably named directory and
+populating it with a ``playbook.yml`` playbook and ``molecule.yml``
+configuration file.
+
+If we are creating an integration test for a module, we can leave the molecule
+configuration file empty. But we must still create the configuration file, or
+Molecule will not detect this scenario. For example, if we would like to
+create a new Molecule scenario that is testing the ``asset`` module, we would
+run this sequence of commands::
+
+ (venv) $ mkdir tests/integration/molecule/module_asset
+ (venv) $ touch tests/integration/molecule/module_asset/{molecule,playbook}.yml
+
+Now we need to add some content to the playbook we just created. There are
+plenty of examples in our test directory if you need some inspiration ;)
+
+Once we have our playbook ready, we can test our scenario by running::
+
+ (venv) $ make tests/integration/molecule/module_asset
+
+We can also run all integration tests with a single command:
+
+ (venv) $ make integration
+
+Do note that this will take about an hour or so, so make sure you have
+something else to do in the mean time ;)
+
+Adding a scenario for role is a bit more complicated since we need to inform
+Molecule what docker images it should use for running tests, but nothing
+drastic.
+
+We run our integration tests as part of our CI process, further reducing the
+chances of broken code getting into our repository.
+
+
+Continuous integration
+----------------------
+
+We use CircleCI to run our test suite on each push and pull request. Our CI
+pipeline is composed of two stages: a fast one and a slow one.
+
+The fast stage is composed of sanity and unit tests. Once those tests are done
+executing and passing, we start execution of the slow stage that runs
+integration tests.
+
+In order to keep the test times as short as possible, the slow stage is
+parallelized. And in order to maximize the benefits of this parallel
+execution, we need to split the work into similarly sized chunks. This is done
+automatically on CircleCI based on the timings from the previous runs.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/hacking/windows.rst b/ansible_collections/sensu/sensu_go/docs/source/hacking/windows.rst
new file mode 100644
index 00000000..6139fa9e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/hacking/windows.rst
@@ -0,0 +1,80 @@
+Windows things
+==============
+
+Working with Windows is a bit different from what most of us are probably used
+to since there is no package manager to delegate installation to and services
+behave a bit differently. Here is a collection of things that might come in
+handy when working with installation and agent role.
+
+
+Adding new Sensu Go version manually
+------------------------------------
+
+As already state, Windows has no built-in package manager backed by a
+repository, which means we have to do a bit more work when installing
+packages. And this is why our installation role contains a few pieces of
+information about the available Sensu Go agent packages.
+
+We can add a new version of Sensu Go for Windows using the following steps:
+
+ 1. Find the version and build we are interested in on packagecloud_.
+ 2. Download the x64 and x86 msi files from the download page. Use the
+ download URL from the `installation guide`_ as a base and adjust as
+ needed.
+ 3. Extract product codes from the msi packages (document author used orca msi
+ editor, would be very much interested in a PowerShell script).
+ 4. Add a new entry to the roles/install/vars/Windows.yml file.
+
+.. _packagecloud: https://packagecloud.io/sensu/stable
+.. _installation guide: https://docs.sensu.io/sensu-go/latest/operations/deploy-sensu/install-sensu/#install-sensu-agents
+
+
+Automating version sync
+-----------------------
+
+Because manually extracting the product code from the msi archive is not
+something we would like to devote our lives to, we created a script that knows
+how to do two things:
+
+ 1. It can check if the variable file is missing any upstream versions.
+ 2. It can automatically update the Windows variable file.
+
+To check for the missing versions in out current file, we can run the following
+command::
+
+ $ tools/windows-versions.py check roles/install/vars/Windows.yml
+ The following versions are missing: 6.2.3.3986, 6.2.4.4013, 6.2.5.4040
+ The following versions are obsolete: 5.20.0.12118, 5.20.1.12427
+
+This command will check if our variable file contains any outdated versions and
+if it is missing any new versions Sensu released since our last update. The
+command also exits with a non-zero status if action is required, which is great
+for CI/CD usage.
+
+We also added the common invocation to the Makefile, which makes running check
+as simple as::
+
+ $ make check_windows_versions
+
+Once we know our variables and upstream availability diverged, we can run the
+update command to get things back in sync::
+
+ $ tools/windows-versions.py update roles/install/vars/Windows.yml
+
+This command will download any missing packages, extract product codes from
+them, and update the variable file. By default, temporary files are put ino the
+``/tmp`` directory, but this is configurable via the ``--cache`` command-line
+switch.
+
+As with the check command, we also added the common invocation to the Makefile,
+which allows us to update the variable file with a simple make run::
+
+ $ make update_windows_versions
+
+.. note::
+
+ The update process needs ``msiinfo`` executable. The package should be
+ available for most mainstream Linux distributions.
+
+Once the update is over, we must commit the changes to the variable file and we
+are done.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/index.rst b/ansible_collections/sensu/sensu_go/docs/source/index.rst
new file mode 100644
index 00000000..4dc189a1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/index.rst
@@ -0,0 +1,53 @@
+Sensu Go Ansible Collection
+===========================
+
+Sensu Go Ansible Collection is a bundle of Ansible content that we can use to
+manage all aspects of Sensu Go. It contains Ansible roles for installing and
+configuring backends and agents. Collection also contains a wide selection of
+modules for runtime management of Sensu Go backend.
+
+Collection is freely available on `Ansible Galaxy`_. For Red Hat subscribers,
+this collection is also available on `Automation Hub`_.
+
+.. _Ansible Galaxy:
+ https://galaxy.ansible.com/sensu/sensu_go
+.. _Automation Hub:
+ https://cloud.redhat.com/ansible/automation-hub/sensu/sensu_go
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Using Sensu Go Ansible Collection
+
+ quickstart-sensu-go-6
+ quickstart-sensu-go-5
+ installation
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Guides
+
+ versioning_sensu_go_installation
+ sensu_go_5_6_migration
+
+
+.. toctree::
+ :maxdepth: 1
+ :caption: References
+
+ roles
+ modules
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Hacker's guides
+
+ hacking
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Appendices
+
+ release_policy
+ release_notes
diff --git a/ansible_collections/sensu/sensu_go/docs/source/installation.rst b/ansible_collections/sensu/sensu_go/docs/source/installation.rst
new file mode 100644
index 00000000..08fbe6fd
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/installation.rst
@@ -0,0 +1,94 @@
+Installation
+============
+
+We can install Sensu Go Ansible collection using the ``ansible-galaxy`` tool
+that comes bundled with Ansible. This tool can install Ansible collections
+from different sources.
+
+
+Installing from Ansible Galaxy
+------------------------------
+
+`Ansible Galaxy`_ is the default source of Ansible collections for the
+``ansible-galaxy`` tool. We can install Sensu Go Ansible collection by
+running::
+
+ $ ansible-galaxy collection install sensu.sensu_go
+
+.. _Ansible Galaxy: https://galaxy.ansible.com
+
+After the command finishes, we will have the latest version of the Sensu Go
+Ansible collection installed and ready to be used.
+
+We can also install a specific version of the collection by appending a
+version after the name::
+
+ $ ansible-galaxy collection install sensu.sensu_go:1.0.0
+
+.. note::
+
+ ``ansible-galaxy`` command will not overwrite the existing collection if it
+ is already installed. We can change this default behavior by adding a
+ ``--force`` command line switch::
+
+ $ ansible-galaxy collection install --force sensu.sensu_go:1.0.0
+
+The official Ansible documentation contains more information about the
+installation options in the `Using collections`_ document.
+
+.. _Using collections:
+ https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#installing-collections
+
+
+Installing from Automation Hub
+------------------------------
+
+If we have a valid Red Hat subscription, we can also install Sensu Go Ansible
+collection from Red Hat Ansible Automation Hub. But before we can do that, we
+need to tell Ansible about the second source of collections. We do this by
+placing the following content into the
+:download:`ansible.cfg <../examples/installation/ansible.cfg>` configuration
+file:
+
+.. literalinclude:: ../examples/installation/ansible.cfg
+ :language: ini
+
+
+Make sure you replace the ``token`` value in the above configuration with the
+value obtained from the `token Automation Hub UI`_.
+
+.. _token Automation Hub UI:
+ https://cloud.redhat.com/ansible/automation-hub/token
+
+From here on, we can follow the steps from the previous section.
+
+
+Installing from a local file
+----------------------------
+
+This last method of installation might come in handy in situations where our
+Ansible control node cannot access Ansible Galaxy or Automation Hub.
+
+First, we need to download the Sensu Go Ansible collection archive from the
+GitHub `releases page`_ and then transfer that archive to the Ansible control
+node. Once we have that archive on our control node, we can install the Sensu
+Go collection by running::
+
+ $ ansible-galaxy collection install path/to/sensu-sensu_go-1.0.0.tar.gz
+
+.. _releases page:
+ https://github.com/sensu/sensu-go-ansible/releases
+
+
+Installing the Windows Ansible Collection
+-----------------------------------------
+
+If we are using Ansible Base or Ansible Core, we need to install the
+`ansible.windows` Ansible Collection manually::
+
+ $ ansible-galaxy collection install ansible.windows
+
+Why is this manual step needed? While it is technically possible to declare
+collection dependencies, this may pose a problem for Automation Hub users.
+The Windows Ansible Collection is not yet certified, so we had to make it an
+optional dependency for the time being.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/modules.rst b/ansible_collections/sensu/sensu_go/docs/source/modules.rst
new file mode 100644
index 00000000..d8f6af46
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/modules.rst
@@ -0,0 +1,190 @@
+Modules
+=======
+
+While different modules perform different tasks, their interfaces all follow
+the same pattern as much as possible. For example, all Sensu Go modules
+support check mode, most of them can have their state set to either
+``present`` or ``absent``, and they identify the resource to operate on using
+the *name* and *namespace* parameters.
+
+The API of each module is composed of two parts. The *auth* parameter contains
+the pieces of information that are related to the Sensu Go backend that the
+module is connecting to. All other parameters hold the information related to
+the resource that we are operating on.
+
+
+Authentication parameters
+-------------------------
+
+Each module has an *auth* parameter that holds the following information about
+the Sensu Go backend:
+
+1. The **url** key holds the address of the Sensu Go backend. If this key is
+ not present in the task's definition, Ansible will consult the *SENSU_URL*
+ environment variable and, if the variable is not set, use the default value
+ of ``http://localhost:8080``.
+2. The **user** and **password** keys contain the credentials that the module
+ will use when connecting to the backend. It not present, Ansible will try
+ to look up the *SENSU_USER* and *SENSU_PASSWORD* environment variables,
+ falling back to the default values of ``admin`` and ``P@ssw0rd!``.
+3. The **api_key** field should contain an `API key`_ that module will use to
+ authenticate with the backend. If not present, Ansible will try to use the
+ value stored in the *SENSU_API_KEY*.
+
+.. _API key:
+ https://docs.sensu.io/sensu-go/latest/guides/use-apikey-feature/
+
+.. note::
+
+ The API key authentication is only available in Sensu Go 5.15 or newer.
+
+When Ansible tries to connect to the Sensu Go backend, it will try to
+authenticate using the API key. If the *api_key* is not set, Ansible will
+fallback to using the *user* and *password* values for authentication. What
+this basically means is that there are two valid sets of *auth* parameters:
+
+.. code-block:: yaml
+
+ - name: Use API key authentication
+ asset:
+ auth:
+ url: http://my.sensu.host:8765
+ api_key: 7f63b5bc-41f4-4b3e-b59b-5431afd7e6a2
+ # Other asset parameters here
+
+ - name: Use user and password authentication
+ asset:
+ auth:
+ url: http://my.sensu.host:8765
+ user: my-user
+ password: my-password
+ # Other asset parameters here
+
+It is not an error to specify all four parameters when writing an Ansible
+task, but the *user* and *password* fields are ignored in this case:
+
+.. code-block:: yaml
+
+ - name: Use API key authentication and ignore user and password values
+ asset:
+ auth:
+ url: http://my.sensu.host:8765
+ user: my-user # IGNORED
+ password: my-password # IGNORED
+ api_key: 7f63b5bc-41f4-4b3e-b59b-5431afd7e6a2
+ # Other asset parameters here
+
+.. note::
+
+ If the *api_key* parameter is set to an invalid value, Ansible will **NOT**
+ fallback to the second method of authentication. Instead, it will report
+ an error and abort the run.
+
+
+Managing Sensu Go resources
+---------------------------
+
+There are three things we can do using the Sensu Go Ansible modules:
+
+1. Make sure that the specified resource is present on the backend.
+2. Make sure that the named resource is not present on the backend.
+3. List all currently available resources on the backend.
+
+.. note::
+
+ We left out the *auth* parameter from the following examples in order to
+ keep them short and readable.
+
+A typical task for creating (and by *creating* we mean *making sure it
+exists*) a resource on the backend would look like this:
+
+.. code-block:: yaml
+
+ - name: Make sure asset is present
+ asset:
+ namespace: my-namespace
+ name: my-asset-name
+ # Other asset parameters go here
+
+We need to specify the resource's name and decide into what namespace to place
+it if the resource is not a cluster-wide resource. There are a few exceptions
+to this rule, but not many.
+
+If we would like to remove a certain resource from the Sensu backend (and by
+*remove* we mean *make sure it is not present*), we can write a task and set
+its *state* parameter to ``absent``:
+
+.. code-block:: yaml
+
+ - name: Make sure asset is absent
+ asset:
+ namespace: my-namespace
+ name: my-asset-name
+ state: absent
+
+Almost every module for manipulating resources has its counterpart module that
+can be used to retrieve information about the corresponding resources, for
+instance *asset* and *asset_info* modules.
+
+.. code-block:: yaml
+
+ - name: Fetch a list of all assets
+ asset_info:
+ namespace: my-namespace
+ register: result
+
+Note the usage of the *asset_info* module in the example above. We can also
+retrieve information about a single asset by adding a *name* parameter to the
+previous task:
+
+.. code-block:: yaml
+
+ - name: Fetch a specific asset
+ asset_info:
+ namespace: my-namespace
+ name: my-asset-name
+ register: result
+
+Info modules always return a list of objects that Sensu API returned. And if
+we try to fetch a non-existing resource, the result will hold an empty list.
+This makes it easy to write conditional tasks using next pattern:
+
+.. code-block:: yaml
+
+ - name: Fetch a specific asset
+ asset_info:
+ namespace: my-namespace
+ name: my-asset-name
+ register: result
+
+ - name: Do something if asset is there
+ debug:
+ msg: We are doing something
+ when: result.objects | length == 1
+
+Of course, you can also loop over the result using a *loop* construct:
+
+.. code-block:: yaml
+
+ - name: Fetch a list of all assets
+ asset_info:
+ namespace: my-namespace
+ register: result
+
+ - name: Display number of builds in an asset
+ debug:
+ msg: "{{ item.metadata.name }}: {{ item.builds | length }}"
+ loop: result.objects
+
+Reference material for each module contains documentation on what parameters
+certain modules accept and what values they expect those parameters to be.
+
+
+Module reference
+----------------
+
+.. toctree::
+ :glob:
+ :maxdepth: 1
+
+ modules/*
diff --git a/ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-5.rst b/ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-5.rst
new file mode 100644
index 00000000..81e21855
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-5.rst
@@ -0,0 +1,42 @@
+Quickstart for Sensu Go 5
+=========================
+
+Before we can do anything, we need to install Sensu Go Ansible Collection.
+Luckily, we are just one short command away from that goal::
+
+ $ ansible-galaxy collection install sensu.sensu_go
+
+Now we can set up a simple Sensu Go sandbox using the following
+:download:`playbook <../examples/quickstart/playbook-5.yaml>`:
+
+.. literalinclude:: ../examples/quickstart/playbook-5.yaml
+ :language: yaml
+
+When we run it, Ansible will install and configure backend and agents on
+selected hosts, and then configure a ntp check that agents will execute twice
+a minute. Note that we do not need to inform agents explicitly where the
+backend is because the :doc:`agent role <roles/agent>` can obtain the
+backend's address from the inventory.
+
+Now, before we can run this playbook, we need to prepare an inventory file.
+The inventory should contain two groups of hosts: *backends* and *agents*. A
+:download:`minimal inventory <../examples/quickstart/inventory.yaml>` with
+only two hosts will look somewhat like this:
+
+.. literalinclude:: ../examples/quickstart/inventory.yaml
+ :language: yaml
+
+Replace the IP addresses with your own and make sure you can ssh into those
+hosts. If you need help with building your inventory file, consult `official
+documentation on inventory`_.
+
+.. _official documentation on inventory:
+ https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
+
+All that we need to do now is to run the playbook::
+
+ $ ansible-playbook -i inventory.yaml playbook-5.yaml
+
+And in a few minutes, things should be ready to go. And if we now visit
+http://192.168.50.4:3000 (replace that IP address with the address of your
+backend), we can log in and start exploring.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-6.rst b/ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-6.rst
new file mode 100644
index 00000000..6a20da3b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/quickstart-sensu-go-6.rst
@@ -0,0 +1,42 @@
+Quickstart for Sensu Go 6
+=========================
+
+Before we can do anything, we need to install Sensu Go Ansible Collection.
+Luckily, we are just one short command away from that goal::
+
+ $ ansible-galaxy collection install sensu.sensu_go
+
+Now we can set up a simple Sensu Go sandbox using the following
+:download:`playbook <../examples/quickstart/playbook-6.yaml>`:
+
+.. literalinclude:: ../examples/quickstart/playbook-6.yaml
+ :language: yaml
+
+When we run it, Ansible will install and configure backend and agents on
+selected hosts, and then configure a ntp check that agents will execute twice
+a minute. Note that we do not need to inform agents explicitly where the
+backend is because the :doc:`agent role <roles/agent>` can obtain the
+backend's address from the inventory.
+
+Now, before we can run this playbook, we need to prepare an inventory file.
+The inventory should contain two groups of hosts: *backends* and *agents*. A
+:download:`minimal inventory <../examples/quickstart/inventory.yaml>` with
+only two hosts will look somewhat like this:
+
+.. literalinclude:: ../examples/quickstart/inventory.yaml
+ :language: yaml
+
+Replace the IP addresses with your own and make sure you can ssh into those
+hosts. If you need help with building your inventory file, consult `official
+documentation on inventory`_.
+
+.. _official documentation on inventory:
+ https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
+
+All that we need to do now is to run the playbook::
+
+ $ ansible-playbook -i inventory.yaml playbook-6.yaml
+
+And in a few minutes, things should be ready to go. And if we now visit
+http://192.168.50.4:3000 (replace that IP address with the address of your
+backend), we can log in and start exploring.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/release_notes.rst b/ansible_collections/sensu/sensu_go/docs/source/release_notes.rst
new file mode 100644
index 00000000..8accd204
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/release_notes.rst
@@ -0,0 +1,412 @@
+Release notes
+=============
+Version 1.13.2 -- Support for latest Ansible
+--------------------------------------------
+Added support for Ansible 2.14. Added support for Alma and Rocky linux
+
+**New features:**
+
+* Added support for ansible 2.14
+* Added support for Alma and Rocky linux
+
+Version 1.13.1 -- Support for latest Ansible
+--------------------------------------------
+
+Added support for Ansible 2.13.
+
+**New features:**
+
+* Added support for ansible 2.13
+* Removed support for CentOS 8
+
+Version 1.13.0 -- Bonsai asset definitions can be downloaded on controller or remote nodes
+------------------------------------------------------------------------------------------
+
+A user can decide to download the Bonsai asset definitions on either the
+controller node (previous functionality) or remote nodes (new functionality).
+A use case mandating such a need is when the controller node's Internet
+connection is unstable or worse than the connection of the remote nodes.
+
+**New features:**
+
+* Add argument remote_on inside bonsai_asset module
+
+Version 1.12.1 -- Sensu Go 6.6.0
+--------------------------------
+
+Added support for Sensu Go 6.5.5 and 6.6.2 on Windows.
+
+**New features:**
+
+* Add Sensu Go 6.5.5 Windows metadata
+* Add Sensu Go 6.6.2 Windows metadata
+
+Version 1.12.0 -- Keeping up with the updates
+---------------------------------------------
+
+In this release, community contributed support for the OracleLinux. We added a
+few tests to catch if things break in the future and this is about it. And
+while we were at work, we also added support for Sensu Go 6.4.0 and 6.4.1 on
+Windows.
+
+**New features:**
+
+* Add support for OracleLinux.
+* Add Sensu Go 6.4.0 Windows metadata.
+* Add Sensu Go 6.4.1 Windows metadata.
+
+
+Version 1.11.1 -- Sensu Go 6.3.0 is here
+----------------------------------------
+
+For this release, we only updated the list of available Sensu Go agent versions
+for Windows, and made sure collection works with the latest Sensu Go version.
+
+**New features:**
+
+* Update list of available Sensu Go agent packages for Windows installations
+ (added 6.3.0).
+
+
+Version 1.11.0 -- Validate all the things!
+------------------------------------------
+
+If you ever thought to yourself, "Ansible does not yell enough at me," we have
+some great news. The Sensu Go Ansible Collection gained role argument
+specifications, making it possible to validate variable values before executing
+a role. You are welcome ;)
+
+**New features:**
+
+* Add argument specification to the install role.
+* Add argument specification to the backend role.
+* Add argument specification to the agent role.
+
+
+Version 1.10.0 -- Authentication, authentication on the wall, Who has Access to Them All?
+-----------------------------------------------------------------------------------------
+
+New modules allow Sensu Go users to configure authentication within
+their Ansible playbooks. The users can authenticate via external
+authentication providers such as Lightweight Directory Access Protocol (LDAP),
+Active Directory (AD), or OpenID Connect 1.0 protocol (OIDC).
+
+**New features:**
+
+* Add modules for managing Sensu Go authentication providers.
+
+
+Version 1.9.4 -- Opening Windows for real
+-----------------------------------------
+
+This is a bugfix release that makes sure Sensu Go Ansible Collection can
+operate even in the absence of the Windows Ansible Collection (assuming we do
+not want to manage agents on Windows hosts, that is).
+
+**Bug fixes:**
+
+* Make sure we lazy-load Windows-related content.
+
+
+Version 1.9.3 -- Opening Windows
+--------------------------------
+
+The only change in this release is removal of the ``ansible.windows``
+dependency. This should allow users that only use certified
+collections to install and use the collection.
+
+**New features:**
+
+* Remove Windows Ansible Collection dependency.
+
+
+Version 1.9.2 -- A fresh batch of updates
+-----------------------------------------
+
+For this release, we only updated the list of available Sensu Go agent versions
+for Windows.
+
+**New features:**
+
+* Update list of available Sensu Go agent packages for Windows installations
+ (added 5.21.4, 5.21.5, 6.2.5, and 6.2.6).
+
+
+Version 1.9.1 -- Containerize all the things
+--------------------------------------------
+
+There are two main reasons for this release. We made sure the Sensu Go Ansible
+Collection works with development version of Ansible (upcoming ansible-core
+2.11). And we added enough metadata to the collection that ansible-builder can
+create an execution environment with the Sensu Go Ansible Collection without
+having to manually specify dependencies.
+
+**Bug fixes:**
+
+* Add ansible.windows dependency that we forgot to add when we introducted the
+ Sensu Go agent installation on Windows.
+
+
+Version 1.9.0 -- Multi-cluster visibility with federation
+---------------------------------------------------------
+
+Two new module pairs allow Sensu Go users to configure federation from the
+comfort of their Ansible playbooks.
+
+**New features:**
+
+* Add modules for managing etcd replicatiors, which form the basis of the Sensu
+ Go federation.
+* Add modules for managing Sensu Go clusters.
+* Update list of available Sensu Go agent packages for Windows installations.
+
+**Bug fixes:**
+
+* Allow downgrading Sensu Go packages on Linux distributions that use yum or
+ dnf for package management.
+
+
+Version 1.8.0 -- Supporting hashed user passwords
+-------------------------------------------------
+
+Starting with this release, Sensu Go users can use password hashes directly
+when manipulating role-based access control resources.
+
+**New features:**
+
+* Add support for hashed password in user module.
+
+**Bug fixes:**
+
+* Mimic actual responses when user module runs in check mode.
+* Make it possible to use modules on Sensu Go backends with no version number.
+
+
+Version 1.7.2 -- Be kind
+------------------------
+
+The main thing in this release is a small adjustment of our code of conduct
+that is a bit more generic and less event-focused.
+
+**Bug fixes:**
+
+* List version 6.2.1 and 6.2.2 in Windows lookup table.
+* Specify minimal python version for modules.
+* Update code of conduct.
+
+
+Version 1.7.1 -- Slow and steady
+--------------------------------
+
+There are no major new features in this release, just honest little fixes that
+should make using Sensu Go Ansible Collection a bit more pleasant.
+
+**Bug fixes:**
+
+* List version 6.2.0 and 6.1.3 in Windows lookup table.
+* Add module return value samples.
+
+
+Version 1.7.0 -- Say hello to Amazon Linux and Windows
+------------------------------------------------------
+
+As the title suggests, we worked hard to bring you two new supported platforms
+to the Sensu Go Ansible Colletions. And yes, all your existing playbooks still
+work.All you need to do is run them against the right host and voila ;)
+
+**New features:**
+
+* Add support for installing Sensu Go on Amazon Linux.
+* Add support for installing Sensu Go agents on Windows.
+
+
+
+Version 1.6.1 -- Comparing entities is hard
+-------------------------------------------
+
+This is a bugfix release that makes sure agent entity changes are properly
+detected.
+
+**Bug fixes:**
+
+* Make subscriptions comparison insensitive to ordering.
+* Make sure agent entities handle *entity:{name}* automatic subscriptions.
+
+
+Version 1.6.0 -- Our little secret
+----------------------------------
+
+This release contains a few new modules that allow you to manage all things
+related to the Sensu Go secrets: from adding secrets providers to passing
+secrets to resources that know how to use them.
+
+**New features:**
+
+* Add modules for managing Sensu Go secret providers.
+* Add modules for managing Sensu Go secrets.
+* Add support for secrets to pipe handler module.
+* Add support for secrets to check module.
+* Add support for secrets to mutator module.
+
+
+Version 1.5.0 -- Self-signed security
+-------------------------------------
+
+The primary focus of this release is to enable configuration of Sensu Go
+backends that use certificates that are not considered trusted when using
+system-provided CA bundle.
+
+**New features:**
+
+* Allow modules to supply custom CA bundle for backend certificate validation
+ or skip the validation entirely.
+
+**Bug fixes:**
+
+* Expand documentation about the *check_hooks* parameter in the check module.
+* Explain how the resource name parameter is used and what invariants need to
+ hold in order for the Sensu Go to consider it a valid name.
+
+Version 1.4.2 -- Break the fall
+-------------------------------
+
+There is really only one reason for this release: making sure user management
+works with Sensu Go 5.21.0 and newer. And while the upstream did break the
+API, we did not, so all your playbooks should function as nothing happened. We
+had to add a *bcrypt* dependency to our collection so make sure it is
+installed on hosts that will execute the user module.
+
+**Bug fixes:**
+
+* Make sure check module is as idempotent as possible.
+* Make user module compatible with Sensu Go >= 5.21.0.
+
+
+Version 1.4.1 -- Maintenance is the name of the game
+----------------------------------------------------
+
+There are no nothing earth-shattering changes in this release, just honest
+little bug fixes and compatibility improvements.
+
+**NOTE:** The *sensu.sensu_go.user* module currently **DOES NOT** work on
+Sensu Go 5.21.0 and later. This is a know issue that will be fixed as soon as
+the updated user-related backend API endpoints are documented.
+
+
+**Bug fixes:**
+
+* Make sure event module always returns a predicted result.
+* Make user module fully-idempotent. Previous versions did not properly detect
+ the password changes.
+* Use fully-qualified collection names in module documentation.
+* Ensure backend initialization properly reports changed state.
+* Make API key authentication work even for regular users with limited
+ permissions.
+* Update the datastore module to cope with the minor API changes.
+
+
+Version 1.4.0 -- Keeping up with the world
+------------------------------------------
+
+Main changes in this release are related to updates in the Sensu Go's web API
+that broke our change detection.
+
+**New features:**
+
+* Add support for RHEL and CentOS 8.
+
+**Bug fixes:**
+
+* Fix resource metadata comparison on Sensu Go 5.19.0 and newer.
+* Update entity comparator to handle new fields.
+
+
+Version 1.3.1 -- Bug fixing galore
+----------------------------------
+
+This release makes it possible to use the *asset* module when replacing the
+deprecated, single-build assets that were created by means other than Ansible.
+
+**Bug fixes:**
+
+* Do not die when encountering a deprecated asset format.
+* Update return value documentation for info modules.
+* Add Sensu Go 5.17.x and 5.18.x to the test suite and remove the unsupported
+ versions (5.14.2 and lower).
+* Update the role metadata with proper platform markers.
+* Remove unsupported Ubuntu versions from the test suite.
+
+
+Version 1.3.0 -- Authenticating with style on Debian
+----------------------------------------------------
+
+Sensu Go 5.15.0 gained an API key authentication method and the Ansible
+collection finally caught up. This means that we can now replace *user* and
+*password* authentication parameters with a single *api_key* value.
+
+And the other big news is the addition of Debian support to the `install`
+role.
+
+**New features:**
+
+* Add API key authentication support.
+* Add support for Debian installation.
+
+
+Version 1.2.0 -- Building support for builds
+--------------------------------------------
+
+This release adds support for specifying builds when installing various Sensu
+Go components.
+
+**New features:**
+
+* Add *build* variable to the *install* role that further pins down the
+ package version that gets installed.
+
+
+Version 1.1.1 -- Python 2 is Still a Thing
+------------------------------------------
+
+This is a bugfix release that makes sure the Sensu collection is working when
+Ansible control node uses Python 2.
+
+**New features:**
+
+* Add support for RHEL 7 to the install role (thanks, @danragnar).
+
+**Bug fixes:**
+
+* Accept *str* and *unicode* instance as a valid string in *bonsai_asset*
+ action plugin.
+
+
+Version 1.1 -- Hello Sensu Go 5.16
+----------------------------------
+
+This is the first release that supports installing Sensu Go 5.16.
+
+**New features:**
+
+* Support for Sensu Go 5.16 initialization in backend role.
+* Support for external datastore management using *datastore* and
+ *datastore_info* modules.
+
+**Bug fixes:**
+
+* Reintroduce namespace support to *bonsai_asset* module (thanks, @jakeo)
+
+
+Version 1.0 -- Rising From The Ashes
+------------------------------------
+
+This is the initial stable release of the Sensu Go Ansible Collection. It
+contains roles for installing and configuring Sensu Go backends and agents and
+a set of modules for managing Sensu Go resources.
+
+Where does the release name comes from? We took an existing Ansible Collection
+that `@flowerysong`_ wrote, gave it a thorough tune-up and added a
+comprehensive test suite. And now, it is ready to face the world!
+
+.. _@flowerysong: https://github.com/flowerysong/ansible-sensu-go
+
diff --git a/ansible_collections/sensu/sensu_go/docs/source/release_policy.rst b/ansible_collections/sensu/sensu_go/docs/source/release_policy.rst
new file mode 100644
index 00000000..decde868
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/release_policy.rst
@@ -0,0 +1,50 @@
+Release policy
+==============
+
+Our release policy could be summarized as:
+
+ 1. We only support latest stable release.
+ 2. We create new bugfix and feature releases as things progress and not on a
+ fixed schedule.
+ 3. We support deprecated content for at least a year and a half since its
+ deprecation.
+ 4. We have no plans on releasing version 2.0.0 anytime soon and are fully
+ commited on making current version of collection work on all supported
+ Sensu Go versions.
+
+
+Supported releases
+------------------
+
+As already stated, we only support latest stable version of Sensu Go Ansible
+Collection. Currently, this means that only latest 1.x.y version is supported.
+
+Once we next major version of collection, we will continue to backport
+security fixes to previous major version 1 for at least half a year. No new
+features will be backported.
+
+
+Release schedule
+----------------
+
+To get new features and bugfixes to our users as soon as possible, we have no
+fixed release schedule. Instead, we create a new release after each
+significant change (bugfix or added feature). We may delay release for a day
+or two if we have a few thing lined up to reduce the administrative load.
+
+
+Compatibility
+-------------
+
+Sensu Go Ansible Collection follows `semantic versioning`_. In short, we
+guarantee backward compatibility between releases with the same major version
+number. In practice, this means that if we have a playbook that works with
+some version of Sensu Go Ansible Collection, it will continue to work with
+newer versions up until major version changes.
+
+.. _semantic versioning: https://semver.org/
+
+We do not guarantee forward compatibility (we are adding new modules in
+releases that increment minor version number). Downgrading between patch
+versions should be safe, but we would advice against it because patch releases
+can potentially contain important security fixes.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/roles.rst b/ansible_collections/sensu/sensu_go/docs/source/roles.rst
new file mode 100644
index 00000000..12293cba
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/roles.rst
@@ -0,0 +1,11 @@
+Roles
+=====
+
+Sensu Go Ansible Collection contains three roles that allow us install and
+configure Sensu Go backend and agents.
+
+.. toctree::
+ :glob:
+ :maxdepth: 1
+
+ roles/*
diff --git a/ansible_collections/sensu/sensu_go/docs/source/roles/agent.rst b/ansible_collections/sensu/sensu_go/docs/source/roles/agent.rst
new file mode 100644
index 00000000..870d59bf
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/roles/agent.rst
@@ -0,0 +1,53 @@
+Sensu Go agent role
+===================
+
+This role installs, configures and starts the ``sensu-agent`` service.
+
+
+Example playbook
+----------------
+
+The most basic
+:download:`agent playbook <../../examples/roles/agent.yaml>` looks like this:
+
+.. literalinclude:: ../../examples/roles/agent.yaml
+ :language: yaml
+
+This playbook will install the latest stable version of the Sensu Go agent
+and configure it. We can customize the agent's configuration by adding more
+options to the *agent_config* variable.
+
+
+Agent configuration options
+---------------------------
+
+The *agent_config* variable can contain any option that is valid for the Sensu
+Go agent version we are installing. All valid options are listed in the
+`official Sensu documentation`_.
+
+.. _official Sensu documentation:
+ https://docs.sensu.io/sensu-go/latest/reference/agent/#configuration
+
+.. note::
+
+ Role copies the key-value pairs from the *agent_config* variable verbatim
+ to the configuration file. This means that we must copy the key names
+ **exactly** as they appear in the configuration reference. In a way, the
+ *agent_config* variable should contain a properly indented copy of the
+ ``/etc/sensu/agent.yml`` file.
+
+
+Tested Platforms (CI/CD)
+------------------------
+
++-------+--------------+-----------------------------------+
+| OS | distribution | versions |
++=======+==============+===================================+
+| Linux | CentOS | 7 |
+| +--------------+-----------------------------------+
+| | RedHat | 7, 8 |
+| +--------------+-----------------------------------+
+| | Debian | 9, 10 |
+| +--------------+-----------------------------------+
+| | Ubuntu | 14.04, 16.04, 18.04 |
++-------+--------------+-----------------------------------+
diff --git a/ansible_collections/sensu/sensu_go/docs/source/roles/backend.rst b/ansible_collections/sensu/sensu_go/docs/source/roles/backend.rst
new file mode 100644
index 00000000..d89c573c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/roles/backend.rst
@@ -0,0 +1,192 @@
+Sensu Go backend role
+=====================
+
+This role installs, configures and starts the ``sensu-backend`` service.
+
+
+Example playbook
+----------------
+
+The most basic
+:download:`backend playbook <../../examples/roles/backend.yaml>` looks like
+this:
+
+.. literalinclude:: ../../examples/roles/backend.yaml
+ :language: yaml
+
+This playbook will install the latest stable version of the Sensu Go backend
+and configure it. We can customize the backend's configuration by adding more
+options to the *backend_config* variable.
+
+
+Backend configuration options
+-----------------------------
+
+The *backend_config* variable can contain any option that is valid for the
+Sensu Go backend version we are installing. All valid options are listed in
+the `official Sensu documentation`_.
+
+.. _official Sensu documentation:
+ https://docs.sensu.io/sensu-go/latest/reference/backend/#configuration
+
+.. note::
+
+ Role copies the key-value pairs from the *backend_config* variable verbatim
+ to the configuration file. This means that we must copy the key names
+ **exactly** as they appear in the configuration reference. In a way, the
+ *backend_config* variable should contain a properly indented copy of the
+ ``/etc/sensu/backend.yml`` file.
+
+Users of Sensu Go >= 5.16 have two additional variables at their disposal that
+control the first-time backend initialization:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 30 45
+
+ * - Variable
+ - Default
+ - Description
+
+ * - cluster_admin_username
+ - admin
+ - Initial admin user to create when initializing backend for the first
+ time.
+
+ * - cluster_admin_password
+ - P@ssw0rd!
+ - Initial admin password to create when initializing backend for the
+ first time.
+
+On Sensu Go version below 5.16, these two variables have no effect since
+default admin credentials are baked into the Sensu Go backend.
+
+
+Securing Sensu Go backend
+-------------------------
+
+This role enables users to establish secure end-to-end communications of the
+components that comprise the Sensu Go backend. The user needs to supply the
+paths to the PKI files by placing the appropriate public and private key files
+somewhere within the Ansible playbook search path. They then need to reference
+these paths in the appropriate inventory variables, as described below.
+
+.. note::
+
+ All of the files referenced in each of the following subsections need to be
+ supplied. If even a single file is missing or not defined, the play will
+ fail. If none of the variables within a subsection is defined, those
+ services will be configured without the secure communication.
+
+Etcd peer communication
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To secure the etcd communication, create the appropriate files for the PKI
+and define **all** of the following variables:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 30 45
+
+ * - Variable
+ - Examples
+ - Description
+
+ * - etcd_cert_file
+ - files/pki/etcd-client.crt
+ - Path to the certificate used for SSL/TLS connections **to** etcd. This
+ is a client certificate.
+
+ * - etcd_key_file
+ - files/pki/etcd-client.key
+ - Path to the private key for the etcd client certificate file. Must be
+ unencrypted.
+
+ * - etcd_trusted_ca_file
+ - files/pki/client-ca.crt
+ - Path to the trusted certificate authority for the etcd client
+ certificates.
+
+ * - etcd_peer_cert_file
+ - files/pki/etcd-peer.crt
+ - Path to the certificate used for SSL/TLS connections between peers.
+ This will be used both for listening on the peer address as well as
+ sending requests to other peers.
+
+ * - etcd_peer_key_file
+ - files/pki/etcd-peer.key
+ - Path to the peer certificate's key. Must be unencrypted.
+
+ * - etcd_peer_trusted_ca_file
+ - files/pki/etcd-peer-ca.crt
+ - Path to the trusted certificate authority for the peer certificates.
+
+Backend API
+^^^^^^^^^^^
+
+To secure the Sensu Go backend API communication, create the appropriate files
+for the PKI and define **all** of the following variables:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 30 45
+
+ * - Variable
+ - Examples
+ - Description
+
+ * - api_cert_file
+ - files/pki/sensu-api.crt
+ - Path to the certificate used to secure the Sensu Go API.
+
+ * - api_key_file
+ - files/pki/sensu-api.key
+ - Path to the private key corresponding to the Sensu Go API certificate.
+ Must be unencrypted.
+
+ * - api_trusted_ca_file
+ - files/pki/sensu-api-ca.crt
+ - Path to the trusted certificate authority for the Sensu Go API
+ certificates.
+
+Dashboard
+^^^^^^^^^
+
+To secure the Sensu dashboard communication, create the appropriate files for the
+PKI and define **all** of the following variables:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 30 45
+
+ * - Variable
+ - Examples
+ - Description
+
+ * - dashboard_cert_file
+ - files/pki/sensu-dashboard.crt
+ - Path to the certificate used for SSL/TLS connections to the dashboard.
+
+ * - dashboard_key_file
+ - files/pki/sensu-dashboard.key
+ - Path to the private key corresponding to the dashboard certificate.
+ Must be unencrypted.
+
+The role will automatically configure the dashboard endpoint to use HTTPS,
+e.g.: `https://localhost:3000`.
+
+
+Tested Platforms (CI/CD)
+------------------------
+
++-------+--------------+-----------------------------------+
+| OS | distribution | versions |
++=======+==============+===================================+
+| Linux | CentOS | 7 |
+| +--------------+-----------------------------------+
+| | RedHat | 7, 8 |
+| +--------------+-----------------------------------+
+| | Debian | 9, 10 |
+| +--------------+-----------------------------------+
+| | Ubuntu | 14.04, 16.04, 18.04 |
++-------+--------------+-----------------------------------+
diff --git a/ansible_collections/sensu/sensu_go/docs/source/roles/install.rst b/ansible_collections/sensu/sensu_go/docs/source/roles/install.rst
new file mode 100644
index 00000000..a06f5188
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/roles/install.rst
@@ -0,0 +1,87 @@
+Sensu Go install role
+=====================
+
+This role installs selected Sensu Go components from the official precompiled
+packages.
+
+.. note::
+
+ This role only configures your package manager (like yum or apt) and
+ installs the binaries. It does **not** configure anything and it does
+ **not** run any services.
+
+
+Example playbook
+----------------
+
+The next :download:`playbook <../../examples/roles/install.yaml>` demonstrates
+how to install different versions of Sensu components from specific channels.
+
+.. literalinclude:: ../../examples/roles/install.yaml
+ :language: yaml
+
+
+Role Variables
+--------------
+
+This role consults the following variables to determine what packages to
+install:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 25 30 45
+
+ * - Variable
+ - Default value
+ - Description
+
+ * - components
+ - - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
+ - List of components to install. Valid values are ``sensu-go-backend``,
+ ``sensu-go-agent`` and ``sensu-go-cli``.
+
+ * - channel
+ - stable
+ - Repository channel that serves as a source of packages. We can see all the
+ available channels on packagecloud_ site.
+
+ * - version
+ - latest
+ - Package version to install. Can be any valid version string such as
+ ``5.14.2`` or special value ``latest``.
+
+ * - build
+ - latest
+ - Package build to install. Can be any valid build string such as
+ ``8290`` or a special value ``latest``. If the *version* variable is
+ set to ``latest``, this variable is ignored and the latest available
+ build is installed.
+
+ * - packagecloud_auth
+ - ""
+ - Credentials to use when setting up Sensu Go repos. For publicly
+ available repos, this can be left empty. For private repos, this should
+ be set to the value of the read token, folloved by the ``:@``. Example:
+ ``ofb9123457acdddef6734524:@``.
+
+.. _packagecloud: https://packagecloud.io/sensu
+
+
+Tested Platforms (CI/CD)
+------------------------
+
++-------+--------------+-----------------------------------+
+| OS | distribution | versions |
++=======+==============+===================================+
+| Linux | Amazon | 1, 2 |
+| +--------------+-----------------------------------+
+| | CentOS | 7 |
+| +--------------+-----------------------------------+
+| | RedHat | 7, 8 |
+| +--------------+-----------------------------------+
+| | Debian | 9, 10 |
+| +--------------+-----------------------------------+
+| | Ubuntu | 14.04, 16.04, 18.04 |
++-------+--------------+-----------------------------------+
diff --git a/ansible_collections/sensu/sensu_go/docs/source/sensu_go_5_6_migration.rst b/ansible_collections/sensu/sensu_go/docs/source/sensu_go_5_6_migration.rst
new file mode 100644
index 00000000..482b422d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/sensu_go_5_6_migration.rst
@@ -0,0 +1,74 @@
+Updating playbooks for Sensu Go 6
+=================================
+
+One of the more significant changes in Sensu Go 6 is how we manage agents and
+agent entities. In previous Sensu Go versions, configuration for both agents and
+agent entities resided in the agent's configuration file. A typical Ansible
+playbook for managing Sensu Go 5 agents looked like this:
+
+.. code-block:: yaml
+
+ - name: Install, configure and run Sensu agents
+ hosts: agents
+ become: true
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ version: 5.21.2
+ agent_config:
+ name: my-agent
+ backend-url:
+ - ws://backend.host:8081
+ keepalive-interval: 5
+ keepalive-timeout: 10
+ deregister: true
+ subscriptions:
+ - linux
+
+In Sensu Go 6, we manage the entity representing the agent via the API, just
+like all other Sensu Go resources. Unfortunately, this means that we must move
+specific configuration options from the ``agent_config`` variable into a
+separate task in our play for managing monitoring configuration:
+
+.. code-block:: yaml
+
+ - name: Install, configure and run Sensu agents
+ hosts: agents
+ become: true
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ version: 6.0.0
+ agent_config:
+ name: my-agent
+ backend-url:
+ - ws://backend.host:8081
+ keepalive-interval: 5
+ keepalive-timeout: 10
+
+ - name: Configure monitoring resources
+ hosts: localhost
+ tasks:
+ - name: Add subscriptions to agent entity
+ sensu.sensu_go.entity:
+ name: my-agent
+ entity_class: agent
+ deregister: true
+ subscriptions:
+ - linux
+
+Options that we should move are **annotations**, **deregister**,
+**deregistration-handler**, **labels**, **redact**, and **subscriptions**. We
+can copy over most of the options from one play into another verbatim. The only
+exception is the **deregistration-handler** agent configuration option that
+corresponds to the **deregistration_handler** entity module parameter.
+
+.. note::
+
+ If we leave entity-related configuration options in the agent's configuration
+ file, Sensu Go will use them when creating an entity. Sensu Go will ignore
+ any subsequent updates of the configuration file.
diff --git a/ansible_collections/sensu/sensu_go/docs/source/versioning_sensu_go_installation.rst b/ansible_collections/sensu/sensu_go/docs/source/versioning_sensu_go_installation.rst
new file mode 100644
index 00000000..009b947e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/source/versioning_sensu_go_installation.rst
@@ -0,0 +1,97 @@
+Versioning Sensu Go installation
+================================
+
+When dealing with software installation, we want to pin the versions of our
+components as tightly as possible. In the first part of this guide, we will
+look at how we can pin the Sensu Go version when using Ansible. We will focus
+our attention on updates in the second part of this guide.
+
+
+Initial install
+---------------
+
+The :doc:`backend </roles/backend>` and :doc:`agent </roles/agent>` roles both
+use the :doc:`install </roles/install>` role for installing required
+components. And the installation Ansible role consults three variables when
+determining what component version to install: **version**, **build**, and
+**channel**.
+
+In the vast majority of scenarios, we can set the **version** variable and
+leave the build and channel variables set to their default values:
+
+.. code-block:: yaml
+
+ - name: Install, configure and run Sensu agents
+ hosts: agents
+ become: true
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ version: 6.1.4
+
+When we run the previous playbook, Ansible will install the latest 6.1.4
+version build from the `stable channel`_. And since stable channel only
+contains one package build per released version, we pinned down the Sensu Go
+version to a single package.
+
+Unstable versions of Sensu Go can have more than one build associated with
+them. In scenarios where we are dealing with prerelease versions, we can use
+the **build** variable to make installation predictable or use an older
+package build:
+
+
+.. code-block:: yaml
+
+ - name: Install, configure and run Sensu agents
+ hosts: agents
+ become: true
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ channel: testing
+ version: 6.2.0
+ build: 3881
+
+.. note::
+
+ The installation role ignores both the **channel** and the **build**
+ variables values when installing the Sensu Go agent on Windows. For the
+ time being, Windows users can only install stable versions with the Sensu
+ Go agent role.
+
+We can also install the latest available version if we omit the version
+variable or set it to ``latest``. But we strongly advise against this approach
+since we may inadvertently update our Sensu Go version when all we wanted to
+do is update our configuration file.
+
+
+Updating existing installation
+------------------------------
+
+If you think updating our Sensu Go installation is as simple as bumping the
+version number and rerunning the playbook, you are almost right. Ansible will
+update and restart the updated Sensu Go backend or agent services, but it will
+not run any version-specific migration tasks.
+
+Thankfully, for most updates, restarting services is all that is needed. For
+the rest of the cases, update instructions are usually straightforward. You
+can find them all in the `Sensu Go documentation`_.
+
+
+Downgrading versions
+--------------------
+
+The Sensu Go Ansible Collection does not support downgrades. You might be able
+to install an older version of Sensu Go, but there are no guarantees that your
+installation will still work.
+
+
+.. _stable channel:
+ https://packagecloud.io/sensu/stable
+
+.. _Sensu Go documentation:
+ https://docs.sensu.io/sensu-go/latest/operations/maintain-sensu/upgrade/
diff --git a/ansible_collections/sensu/sensu_go/docs/templates/module.rst.j2 b/ansible_collections/sensu/sensu_go/docs/templates/module.rst.j2
new file mode 100644
index 00000000..2a400c24
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/docs/templates/module.rst.j2
@@ -0,0 +1,108 @@
+:github_url: https://github.com/sensu/sensu-go-ansible/blob/master/plugins/modules/{{ module }}.py
+
+.. _sensu.sensu_go.{{ module }}_module:
+
+{% set title = module + ' -- ' + short_description | rst_ify %}
+{{ title }}
+{{ '=' * title | length }}
+
+{% for desc in description %}
+{{ desc | rst_ify }}
+
+{% endfor %}
+
+{% if version_added is defined -%}
+.. versionadded:: {{ version_added }}
+{% endif %}
+
+{% if requirements -%}
+Requirements
+------------
+
+The below requirements are needed on the host that executes this module:
+
+{% for req in requirements %}
+- {{ req | rst_ify }}
+{% endfor %}
+{% endif %}
+
+
+Examples
+--------
+
+.. code-block:: yaml+jinja
+
+{{ examples | indent(3, True) }}
+
+
+{% if notes -%}
+Notes
+-----
+
+.. note::
+{% for note in notes %}
+ {{ note | rst_ify }}
+
+{% endfor %}
+{% endif %}
+
+
+{% if seealso -%}
+See Also
+--------
+
+.. seealso::
+
+{% for item in seealso %}
+ - :ref:`{{ item.module }}_module`
+{% endfor %}
+{% endif %}
+
+
+{% macro option_desc(opts, level) %}
+{% for name, spec in opts | dictsort recursive %}
+{% set req = "required" if spec.required else "optional" %}
+{% set default = ", default: " ~ spec.default if spec.default else "" %}
+{{ " " * level }}{{ name }} ({{ req }})
+{% for para in spec.description %}
+ {{ " " * level }}{{ para | rst_ify }}
+
+{% endfor %}
+ {{ " " * level }}| **type**: {{ spec.type | default("str") }}
+{% if spec.default %}
+ {{ " " * level }}| **default**: {{ spec.default }}
+{% endif %}
+{% if spec.choices %}
+ {{ " " * level }}| **choices**: {{ ", ".join(spec.choices) }}
+{% endif %}
+
+{% if spec.suboptions %}
+{{ option_desc(spec.suboptions, level + 1) }}
+{% endif %}
+{% endfor %}
+{% endmacro %}
+
+{% if options -%}
+Parameters
+----------
+
+{{ option_desc(options, 0) }}
+{% endif %}
+
+{% if returndocs -%}
+Return Values
+-------------
+
+{% for name, spec in returndocs.items() %}
+{{ name }}
+{% for para in spec.description %}
+ {{ para | rst_ify }}
+
+{% endfor %}
+ **sample**:
+
+ .. code-block:: yaml
+
+ {{ spec.sample | to_yaml(default_flow_style=False, indent=2) | indent(6) }}
+{% endfor %}
+{% endif %}
diff --git a/ansible_collections/sensu/sensu_go/integration.requirements b/ansible_collections/sensu/sensu_go/integration.requirements
new file mode 100644
index 00000000..5d073458
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/integration.requirements
@@ -0,0 +1,6 @@
+molecule ~= 3.2.3
+# Temporary fix until molecule-docker can only support Ansible >= 2.9
+git+https://github.com/xlab-steampunk/molecule-docker.git@temp-fixes#egg=molecule-docker
+pytest
+# Temporary fix until pytest-molecule is updated to work with ansible-core
+git+https://github.com/xlab-steampunk/pytest-molecule.git@temp-fixes#egg=pytest-molecule
diff --git a/ansible_collections/sensu/sensu_go/meta/execution-environment.yml b/ansible_collections/sensu/sensu_go/meta/execution-environment.yml
new file mode 100644
index 00000000..0c2bd227
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/meta/execution-environment.yml
@@ -0,0 +1,4 @@
+---
+version: 1
+dependencies:
+ python: collection.requirements
diff --git a/ansible_collections/sensu/sensu_go/meta/runtime.yml b/ansible_collections/sensu/sensu_go/meta/runtime.yml
new file mode 100644
index 00000000..25ff974c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/meta/runtime.yml
@@ -0,0 +1,2 @@
+---
+requires_ansible: ">=2.9.0"
diff --git a/ansible_collections/sensu/sensu_go/plugins/action/bonsai_asset.py b/ansible_collections/sensu/sensu_go/plugins/action/bonsai_asset.py
new file mode 100644
index 00000000..74473aeb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/action/bonsai_asset.py
@@ -0,0 +1,120 @@
+# -*- 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
+
+from ansible.module_utils.six import text_type
+from ansible.plugins.action import ActionBase
+from ansible.utils.vars import merge_hash
+
+from ..module_utils import bonsai, errors
+
+
+def validate(name, args, required, typ):
+ """
+ Make sure that required values are not None and that if the value is
+ present, it is of the correct type.
+ """
+ value = args.get(name)
+ if required and value is None:
+ raise errors.Error("{0} is required argument".format(name))
+ if value is not None and not isinstance(value, typ):
+ raise errors.Error("{0} should be {1}".format(name, typ))
+
+
+class ActionModule(ActionBase):
+
+ _VALID_ARGS = frozenset((
+ "auth", "name", "version", "namespace", "rename", "labels",
+ "annotations", "on_remote",
+ ))
+
+ def run(self, _tmp=None, task_vars=None):
+ self._supports_check_mode = True
+ self._supports_async = True
+
+ result = super(ActionModule, self).run(task_vars=task_vars)
+
+ wrap_async = (
+ self._task.async_val and not self._connection.has_native_async
+ )
+
+ try:
+ self.validate_arguments(self._task.args)
+ asset = self.download_asset_definition(
+ self._task.args.get("on_remote", False),
+ self._task.args["name"],
+ self._task.args["version"],
+ task_vars,
+ )
+ asset_args = self.build_asset_args(self._task.args, asset)
+ return merge_hash(
+ result,
+ self._execute_module(
+ module_name="sensu.sensu_go.asset", module_args=asset_args,
+ task_vars=task_vars, wrap_async=wrap_async,
+ ),
+ )
+ except errors.Error as e:
+ return dict(result, failed=True, msg=str(e))
+ finally:
+ if not wrap_async:
+ self._remove_tmp_path(self._connection._shell.tmpdir)
+
+ @staticmethod
+ def validate_arguments(args):
+ # We only validate arguments that we use. We let the asset module
+ # validate the rest (like auth data).
+
+ # Next three string validations might seem strange at first, but there
+ # is a reason for this strangenes. On python 2, we should consider
+ # string to be instance of str or unicode. On python 3, strings are
+ # always instances of str. In order to avoid having a separate
+ # validate calls for python 2 and python 3, we always pass a pair of
+ # types that just happen to be the same on python 3.
+ validate("name", args, required=True, typ=(str, text_type))
+ validate("version", args, required=True, typ=(str, text_type))
+ validate("rename", args, required=False, typ=(str, text_type))
+ validate("labels", args, required=False, typ=dict)
+ validate("annotations", args, required=False, typ=dict)
+ validate("on_remote", args, required=False, typ=bool)
+
+ def download_asset_definition(self, on_remote, name, version, task_vars):
+ if not on_remote:
+ return bonsai.get_asset_parameters(name, version)
+
+ args = dict(name=name, version=version)
+ result = self._execute_module(
+ module_name="sensu.sensu_go.bonsai_asset", module_args=args,
+ task_vars=task_vars, wrap_async=False,
+ )
+ if result.get("failed", False):
+ raise errors.Error(result["msg"])
+
+ return result["asset"]
+
+ @staticmethod
+ def build_asset_args(args, bonsai_args):
+ asset_args = dict(
+ name=args.get("rename", args["name"]),
+ state="present",
+ builds=bonsai_args["builds"],
+ )
+
+ if "auth" in args:
+ asset_args["auth"] = args["auth"]
+
+ if "namespace" in args:
+ asset_args["namespace"] = args["namespace"]
+
+ # Only add optional parameter if it is present in at least one source.
+ for meta in ("labels", "annotations"):
+ if bonsai_args[meta] or args.get(meta):
+ asset_args[meta] = merge_hash(
+ bonsai_args[meta] or {}, args.get(meta, {}),
+ )
+
+ return asset_args
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/annotations.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/annotations.py
new file mode 100644
index 00000000..3e93c948
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/annotations.py
@@ -0,0 +1,20 @@
+# -*- 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ annotations:
+ description:
+ - Custom metadata fields with fewer restrictions, as key/value pairs.
+ - These are preserved by Sensu but not accessible as tokens or
+ identifiers, and are mainly intended for use with external tools.
+ type: dict
+ default: {}
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/auth.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/auth.py
new file mode 100644
index 00000000..4fe8aee0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/auth.py
@@ -0,0 +1,75 @@
+# -*- 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ auth:
+ description:
+ - Authentication parameters. Can define each of them with ENV as well.
+ type: dict
+ suboptions:
+ user:
+ description:
+ - The username to use for connecting to the Sensu API.
+ If this is not set the value of the SENSU_USER environment
+ variable will be checked.
+ - This parameter is ignored if the I(auth.api_key) parameter is set.
+ type: str
+ default: admin
+ password:
+ description:
+ - The Sensu user's password.
+ If this is not set the value of the SENSU_PASSWORD environment
+ variable will be checked.
+ - This parameter is ignored if the I(auth.api_key) parameter is set.
+ type: str
+ default: P@ssw0rd!
+ url:
+ description:
+ - Location of the Sensu backend API.
+ If this is not set the value of the SENSU_URL environment variable
+ will be checked.
+ type: str
+ default: http://localhost:8080
+ api_key:
+ description:
+ - The API key that should be used when authenticating. If this is
+ not set, the value of the SENSU_API_KEY environment variable will
+ be checked.
+ - This replaces I(auth.user) and I(auth.password) parameters.
+ - For more information about the API key, refer to the official
+ Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/guides/use-apikey-feature/).
+ type: str
+ version_added: 1.3.0
+ verify:
+ description:
+ - Flag that controls the certificate validation.
+ - If you are using self-signed certificates, you can set this
+ parameter to C(false).
+ - ONLY USE THIS PARAMETER IN DEVELOPMENT SCENARIOS! In you use
+ self-signed certificates in production, see the I(auth.ca_path)
+ parameter.
+ - It is also possible to set this parameter via the I(SENSU_VERIFY)
+ environment variable.
+ type: bool
+ default: true
+ version_added: 1.5.0
+ ca_path:
+ description:
+ - Path to the CA bundle that should be used to validate the backend
+ certificate.
+ - If this parameter is not set, module will use the CA bundle that
+ python is using.
+ - It is also possible to set this parameter via the I(SENSU_CA_PATH)
+ environment variable.
+ type: path
+ version_added: 1.5.0
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/info.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/info.py
new file mode 100644
index 00000000..32614368
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/info.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ name:
+ description:
+ - Retrieve information about this specific object instead of listing all objects.
+ type: str
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/labels.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/labels.py
new file mode 100644
index 00000000..849cd85d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/labels.py
@@ -0,0 +1,19 @@
+# -*- 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ labels:
+ description:
+ - Custom metadata fields that can be accessed within Sensu, as key/value
+ pairs.
+ type: dict
+ default: {}
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/name.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/name.py
new file mode 100644
index 00000000..68efc27a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/name.py
@@ -0,0 +1,25 @@
+# -*- 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ name:
+ description:
+ - The Sensu resource's name. This name (in combination with the
+ namespace where applicable) uniquely identifies the resource that
+ Ansible operates on.
+ - If the resource with selected name already exists, Ansible module will
+ update it to match the specification in the task.
+ - Consult the I(name) metadata attribute specification in the upstream
+ docs on U(https://docs.sensu.io/sensu-go/latest/reference/) for
+ more details about valid names and other restrictions.
+ type: str
+ required: yes
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/namespace.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/namespace.py
new file mode 100644
index 00000000..169a1ee0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/namespace.py
@@ -0,0 +1,19 @@
+# -*- 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ namespace:
+ description:
+ - RBAC namespace to operate in. If this is not set the value of the
+ SENSU_NAMESPACE environment variable will be used.
+ type: str
+ default: default
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/requirements.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/requirements.py
new file mode 100644
index 00000000..15ca542b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/requirements.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2021, 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
+
+
+class ModuleDocFragment(object):
+ # We have an empty options key because ansible fails without it.
+ DOCUMENTATION = """
+options: {}
+requirements:
+ - python >= 2.7
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/secrets.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/secrets.py
new file mode 100644
index 00000000..44c48145
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/secrets.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ secrets:
+ description:
+ - List of secrets that are available to the command.
+ type: list
+ elements: dict
+ version_added: 1.6.0
+ suboptions:
+ name:
+ description:
+ - Variable name that will contain the sensitive data.
+ type: str
+ required: true
+ version_added: 1.6.0
+ secret:
+ description:
+ - Name of the secret that contains sensitive data.
+ type: str
+ required: true
+ version_added: 1.6.0
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/doc_fragments/state.py b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/state.py
new file mode 100644
index 00000000..513c2065
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/doc_fragments/state.py
@@ -0,0 +1,19 @@
+# -*- 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
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = """
+options:
+ state:
+ description:
+ - Target state of the Sensu object.
+ type: str
+ choices: [ present, absent ]
+ default: present
+"""
diff --git a/ansible_collections/sensu/sensu_go/plugins/filter/backends.py b/ansible_collections/sensu/sensu_go/plugins/filter/backends.py
new file mode 100644
index 00000000..ecafd458
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/filter/backends.py
@@ -0,0 +1,28 @@
+# -*- 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
+
+
+def _format_backend(vars):
+ if "api_key_file" in vars:
+ protocol = "wss"
+ else:
+ protocol = "ws"
+ return "{0}://{1}:{2}".format(protocol, vars["inventory_hostname"], 8081)
+
+
+def backends(hostvars, groups):
+ return [
+ _format_backend(hostvars[name]) for name in groups.get("backends", [])
+ ]
+
+
+class FilterModule(object):
+ def filters(self):
+ return dict(
+ backends=backends,
+ )
diff --git a/ansible_collections/sensu/sensu_go/plugins/filter/package_name.py b/ansible_collections/sensu/sensu_go/plugins/filter/package_name.py
new file mode 100644
index 00000000..80e9a875
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/filter/package_name.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# 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
+
+
+def _apt_package_name(name, version, build):
+ if version == "latest":
+ return name
+ if build == "latest":
+ return "{0}={1}-*".format(name, version)
+ return "{0}={1}-{2}".format(name, version, build)
+
+
+def _yum_package_name(name, version, build):
+ if version == "latest":
+ return name
+ if build == "latest":
+ return "{0}-{1}".format(name, version)
+ return "{0}-{1}-{2}".format(name, version, build)
+
+
+KIND_HANDLERS = dict(
+ apt=_apt_package_name,
+ yum=_yum_package_name,
+)
+
+
+def package_name(kind, name, version, build):
+ return KIND_HANDLERS[kind](name, version, build)
+
+
+class FilterModule(object):
+ def filters(self):
+ return dict(
+ package_name=package_name,
+ )
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/arguments.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/arguments.py
new file mode 100644
index 00000000..aed632dc
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/arguments.py
@@ -0,0 +1,120 @@
+# -*- 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
+
+from ansible.module_utils.basic import env_fallback
+
+from . import client
+
+
+SHARED_SPECS = dict(
+ auth=dict(
+ type="dict",
+ apply_defaults=True,
+ options=dict(
+ user=dict(
+ default="admin",
+ fallback=(env_fallback, ["SENSU_USER"]),
+ ),
+ password=dict(
+ default="P@ssw0rd!",
+ no_log=True,
+ fallback=(env_fallback, ["SENSU_PASSWORD"]),
+ ),
+ url=dict(
+ default="http://localhost:8080",
+ fallback=(env_fallback, ["SENSU_URL"]),
+ ),
+ api_key=dict(
+ fallback=(env_fallback, ["SENSU_API_KEY"]),
+ no_log=True,
+ ),
+ verify=dict(
+ default=True,
+ fallback=(env_fallback, ["SENSU_VERIFY"]),
+ type="bool",
+ ),
+ ca_path=dict(
+ fallback=(env_fallback, ["SENSU_CA_PATH"]),
+ type="path",
+ ),
+ ),
+ ),
+ state=dict(
+ default="present",
+ choices=["present", "absent"],
+ ),
+ name=dict(
+ required=True,
+ ),
+ namespace=dict(
+ default="default",
+ fallback=(env_fallback, ["SENSU_NAMESPACE"]),
+ ),
+ labels=dict(
+ type="dict",
+ default={},
+ ),
+ annotations=dict(
+ type="dict",
+ default={},
+ ),
+ secrets=dict(
+ type="list",
+ elements="dict",
+ no_log=False,
+ options=dict(
+ name=dict(type="str", required=True),
+ secret=dict(type="str", required=True, no_log=False),
+ ),
+ ),
+)
+
+
+def get_spec(*param_names):
+ return dict((p, SHARED_SPECS[p]) for p in param_names)
+
+
+def get_spec_payload(source, *wanted_params):
+ return dict(
+ (k, source[k]) for k in wanted_params if source.get(k) is not None
+ )
+
+
+def get_renamed_spec_payload(source, param_mapping):
+ return dict(
+ (n, source[k]) for k, n in param_mapping.items()
+ if source.get(k) is not None
+ )
+
+
+def get_mutation_payload(source, *wanted_params):
+ payload = get_spec_payload(source, *wanted_params)
+ payload["metadata"] = dict(
+ name=source["name"],
+ )
+ # Cluster-wide objects are not limited to a single namespace. This is why we set
+ # metadata.namespace field only if namespace is present in parameters.
+ if "namespace" in source:
+ if not source["namespace"]:
+ # We are raising an exception here for the sake of sanity test.
+ raise AssertionError("BUG: namespace should not be None")
+ payload["metadata"]["namespace"] = source["namespace"]
+
+ for kind in "labels", "annotations":
+ if source.get(kind):
+ payload["metadata"][kind] = dict(
+ (k, str(v)) for k, v in source[kind].items()
+ )
+ return payload
+
+
+def get_sensu_client(auth):
+ return client.Client(
+ auth["url"], auth["user"], auth["password"], auth["api_key"],
+ auth["verify"], auth["ca_path"],
+ )
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/bonsai.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/bonsai.py
new file mode 100644
index 00000000..b7962cf8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/bonsai.py
@@ -0,0 +1,67 @@
+# -*- 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
+
+from . import errors, http
+
+
+def get(path):
+ url = "https://bonsai.sensu.io/api/v1/assets/{0}".format(path)
+ resp = http.request("GET", url)
+
+ if resp.status != 200:
+ raise errors.BonsaiError(
+ "Server returned status {0}".format(resp.status),
+ )
+ if resp.json is None:
+ raise errors.BonsaiError("Server returned invalid JSON document")
+
+ return resp.json
+
+
+def get_available_asset_versions(namespace, name):
+ asset_data = get("{0}/{1}".format(namespace, name))
+ try:
+ return set(v["version"] for v in asset_data["versions"])
+ except (TypeError, KeyError):
+ raise errors.BonsaiError(
+ "Cannot extract versions from {0}".format(asset_data),
+ )
+
+
+def get_asset_version_builds(namespace, name, version):
+ asset = get("{0}/{1}/{2}/release_asset_builds".format(
+ namespace, name, version,
+ ))
+ if "spec" not in asset or "builds" not in asset["spec"]:
+ raise errors.BonsaiError("Invalid build spec: {0}".format(asset))
+ return asset
+
+
+def get_asset_parameters(name, version):
+ try:
+ namespace, asset_name = name.split("/")
+ except ValueError:
+ raise errors.BonsaiError(
+ "Bonsai asset names should be formatted as <namespace>/<name>.",
+ )
+
+ available_versions = get_available_asset_versions(namespace, asset_name)
+ if version not in available_versions:
+ raise errors.BonsaiError(
+ "Version {0} is not available. Choose from: {1}.".format(
+ version, ", ".join(available_versions),
+ ),
+ )
+
+ asset_builds = get_asset_version_builds(namespace, asset_name, version)
+
+ return dict(
+ labels=asset_builds.get("metadata", {}).get("labels"),
+ annotations=asset_builds.get("metadata", {}).get("annotations"),
+ builds=asset_builds["spec"]["builds"],
+ )
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/client.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/client.py
new file mode 100644
index 00000000..cd53077d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/client.py
@@ -0,0 +1,137 @@
+# -*- 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
+
+try:
+ from ansible.module_utils.compat import version
+except ImportError:
+ from distutils import version
+
+from . import errors, http
+
+
+class Client:
+ BAD_VERSION = version.StrictVersion("9999.99.99")
+
+ def __init__(self, address, username, password, api_key, verify, ca_path):
+ self.address = address.rstrip("/")
+ self.username = username
+ self.password = password
+ self.api_key = api_key
+ self.verify = verify
+ self.ca_path = ca_path
+
+ self._auth_header = None # Login when/if required
+ self._version = None # Set version only if the consumer needs it
+
+ @property
+ def auth_header(self):
+ if not self._auth_header:
+ self._auth_header = self._login()
+ return self._auth_header
+
+ @property
+ def version(self):
+ if self._version is None:
+ resp = self.get("/version")
+ if resp.status != 200:
+ raise errors.SensuError(
+ "Version API returned status {0}".format(resp.status),
+ )
+ if resp.json is None:
+ raise errors.SensuError(
+ "Version API did not return a valid JSON",
+ )
+ if "sensu_backend" not in resp.json:
+ raise errors.SensuError(
+ "Version API did not return backend version",
+ )
+ try:
+ self._version = version.StrictVersion(
+ resp.json["sensu_backend"].split("#")[0]
+ )
+ except ValueError:
+ # Backend has no version compiled in - we are probably running
+ # againts self-compiled version from git.
+ self._version = self.BAD_VERSION
+
+ return self._version
+
+ def _login(self):
+ if self.api_key:
+ return self._api_key_login()
+ return self._username_password_login()
+
+ def _api_key_login(self):
+ # We cannot validate the API key because there is no API endpoint that
+ # we could hit for verification purposes. This means that the error
+ # reporting will be a mess but there is not much we can do here.
+ return dict(Authorization="Key {0}".format(self.api_key))
+
+ def _username_password_login(self):
+ resp = http.request(
+ "GET", "{0}/auth".format(self.address), force_basic_auth=True,
+ url_username=self.username, url_password=self.password,
+ validate_certs=self.verify, ca_path=self.ca_path,
+ )
+
+ if resp.status != 200:
+ raise errors.SensuError(
+ "Authentication call returned status {0}".format(resp.status),
+ )
+
+ if resp.json is None:
+ raise errors.SensuError(
+ "Authentication call did not return a valid JSON",
+ )
+
+ if "access_token" not in resp.json:
+ raise errors.SensuError(
+ "Authentication call did not return access token",
+ )
+
+ return dict(
+ Authorization="Bearer {0}".format(resp.json["access_token"]),
+ )
+
+ def request(self, method, path, payload=None):
+ url = self.address + path
+ headers = self.auth_header
+
+ response = http.request(
+ method, url, payload=payload, headers=headers,
+ validate_certs=self.verify, ca_path=self.ca_path,
+ )
+
+ if response.status in (401, 403):
+ raise errors.SensuError(
+ "Authentication problem. Verify your credentials."
+ )
+
+ return response
+
+ def get(self, path):
+ return self.request("GET", path)
+
+ def put(self, path, payload):
+ return self.request("PUT", path, payload)
+
+ def delete(self, path):
+ return self.request("DELETE", path)
+
+ def validate_auth_data(self, username, password):
+ resp = http.request(
+ "GET", "{0}/auth/test".format(self.address),
+ force_basic_auth=True, url_username=username,
+ url_password=password, validate_certs=self.verify,
+ ca_path=self.ca_path,
+ )
+ if resp.status not in (200, 401):
+ raise errors.SensuError(
+ "Authentication test returned status {0}".format(resp.status),
+ )
+ return resp.status == 200
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/debug.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/debug.py
new file mode 100644
index 00000000..25bee388
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/debug.py
@@ -0,0 +1,45 @@
+# -*- 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 os
+import tempfile
+from datetime import datetime
+
+
+DEBUG = os.environ.get("SENSU_ANSIBLE_DEBUG", "").lower() in ["yes", "true"]
+
+
+def log(message, *args, **kwargs):
+ """
+ Log message to a file (/tmp/sensu-ansible.log) at remote target
+
+ Sensu API returns fairly modest error messages (e.g. when PUT payload contains
+ unsupported parameter, the error message won't tell you which one) and that
+ makes it difficult to debug. For that reason we decided to support at least
+ the most primitive type of logging: write to /tmp/sensu-ansible.log file.
+ Beware the log file resides on Ansible target and not host because this is
+ where the module gets executed.
+
+ This function won't do anything unless target has environment variable
+ SENSU_ANSIBLE_DEBUG set to "yes". When troubleshooting, just set the env
+ variable in the playbook.
+ """
+ if DEBUG:
+ with open(os.path.join(tempfile.gettempdir(), "sensu-ansible.log"), "a") as f:
+ f.write("[{0}]: {1}\n".format(datetime.utcnow(), message.format(*args, **kwargs)))
+
+
+def log_request(method, url, payload, resp=None, comment=None):
+ """Log API request and response"""
+ if DEBUG:
+ if resp:
+ code, data = resp.status, resp.data
+ else:
+ code = data = "?"
+ fmt = "{0} {1} {2}\nPAYLOAD:{3}\nRESPONSE:{4}\nCOMMENT:{5}"
+ log(fmt, code, method, url, payload, data, comment)
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/errors.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/errors.py
new file mode 100644
index 00000000..5be0675c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/errors.py
@@ -0,0 +1,27 @@
+# -*- 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
+
+
+class Error(Exception):
+ """ Base error that serves as a parent for all other errors. """
+
+
+class HttpError(Error):
+ """ Error that signals failure in HTTP connection. """
+
+
+class SyncError(Error):
+ """ Error that signals failure when syncing state with remote. """
+
+
+class SensuError(Error):
+ """ Error that signals problems with Sensu Go web API. """
+
+
+class BonsaiError(Error):
+ """ Error that signals problems with Bonsai assets. """
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/http.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/http.py
new file mode 100644
index 00000000..054fa766
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/http.py
@@ -0,0 +1,71 @@
+# -*- 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 json
+
+try:
+ from ssl import CertificateError
+except ImportError:
+ # This will never match the ssl exception, which will cause exception to
+ # bubble up the call stack.
+ class CertificateError(Exception):
+ pass
+
+from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
+from ansible.module_utils.urls import open_url
+
+from . import errors, debug
+
+
+class Response:
+ def __init__(self, status, data):
+ self.status = status
+ self.data = data
+ self._json = None
+
+ @property
+ def json(self):
+ if self._json is None:
+ try:
+ self._json = json.loads(self.data)
+ except TypeError: # Handle python 3.5 returning bytes
+ try:
+ self._json = json.loads(self.data.decode('utf-8'))
+ except ValueError:
+ self._json = None
+ except ValueError: # Cannot use JSONDecodeError here (python 2)
+ self._json = None
+
+ return self._json
+
+
+def request(method, url, payload=None, data=None, headers=None, **kwargs):
+ if payload is not None:
+ data = json.dumps(payload, separators=(",", ":"))
+ headers = dict(headers or {}, **{"content-type": "application/json"})
+
+ try:
+ raw_resp = open_url(
+ method=method, url=url, data=data, headers=headers, **kwargs
+ )
+ resp = Response(raw_resp.getcode(), raw_resp.read())
+ debug.log_request(method, url, payload, resp)
+ return resp
+ except HTTPError as e:
+ # This is not an error, since client consumers might be able to
+ # work around/expect non 20x codes.
+ resp = Response(e.code, e.reason)
+ debug.log_request(method, url, payload, resp)
+ return resp
+ except URLError as e:
+ debug.log_request(method, url, payload, comment=e.reason)
+ raise errors.HttpError(
+ "{0} request failed: {1}".format(method, e.reason),
+ )
+ except CertificateError as e:
+ raise errors.HttpError("Certificate error: {0}".format(e))
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/role_utils.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/role_utils.py
new file mode 100644
index 00000000..4079473f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/role_utils.py
@@ -0,0 +1,76 @@
+# -*- 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
+
+from . import utils
+
+
+def validate_module_params(params):
+ if params['state'] == 'present':
+ if not params['rules']:
+ return 'state is present but all of the following are missing: rules'
+ return None
+
+
+def validate_binding_module_params(params):
+ if params["state"] == "present":
+ if not (params["users"] or params["groups"]):
+ return 'missing required parameters: users or groups'
+
+
+def type_name_dict(obj_type, name):
+ return {
+ 'type': obj_type,
+ 'name': name,
+ }
+
+
+def build_subjects(groups, users):
+ groups_dicts = [type_name_dict('Group', g) for g in (groups or [])]
+ users_dicts = [type_name_dict('User', u) for u in (users or [])]
+
+ return groups_dicts + users_dicts
+
+
+def do_role_bindings_differ(current, desired):
+ if _do_subjects_differ(current['subjects'], desired['subjects']):
+ return True
+
+ return utils.do_differ(current, desired, 'subjects')
+
+
+# sorts a list of subjects (dicts returned by type_name_dict)
+# by 'type' and 'name' keys and returns the result of comparison.
+def _do_subjects_differ(a, b):
+ sorted_a = sorted(a, key=lambda x: (x['type'], x['name']))
+ sorted_b = sorted(b, key=lambda x: (x['type'], x['name']))
+ return sorted_a != sorted_b
+
+
+def _rule_set(rules):
+ return set(
+ (
+ frozenset(r.get('verbs', []) or []),
+ frozenset(r.get('resources', []) or []),
+ frozenset(r.get('resource_names', []) or [])
+ ) for r in rules
+ )
+
+
+def _do_rules_differ(current_rules, desired_rules):
+ if len(current_rules) != len(desired_rules):
+ return True
+ if _rule_set(current_rules) != _rule_set(desired_rules):
+ return True
+ return False
+
+
+def do_roles_differ(current, desired):
+ if _do_rules_differ(current['rules'], desired['rules']):
+ return True
+
+ return utils.do_differ(current, desired, 'rules')
diff --git a/ansible_collections/sensu/sensu_go/plugins/module_utils/utils.py b/ansible_collections/sensu/sensu_go/plugins/module_utils/utils.py
new file mode 100644
index 00000000..bf97120f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/module_utils/utils.py
@@ -0,0 +1,176 @@
+# -*- 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
+
+from ansible.module_utils.six.moves.urllib.parse import quote
+
+from . import errors
+
+
+def do_differ(current, desired, *ignored_keys):
+ if "metadata" in desired:
+ # We treat metadata as any regular dict but ignore the created_by
+ # value since this piece of data is autogenerated on the backend and
+ # must be skipped in the comparison.
+ if do_differ(current["metadata"], desired["metadata"], "created_by"):
+ return True
+
+ ignored_keys = ignored_keys + ("metadata",)
+
+ for key, value in desired.items():
+ if key in ignored_keys:
+ continue
+
+ if value != current.get(key):
+ return True
+
+ return False
+
+
+def do_differ_v1(current, desired, *ignored_keys):
+ # Default comparator for v1 API (mostly enterprise features)
+
+ if "metadata" in desired:
+ # We treat metadata as any regular dict but ignore the created_by
+ # value since this piece of data is autogenerated on the backend and
+ # must be skipped in the comparison.
+ if do_differ(current["metadata"], desired["metadata"], "created_by"):
+ return True
+
+ for key, value in desired.get("spec", {}).items():
+ if key in ignored_keys:
+ continue
+
+ if value != current["spec"].get(key):
+ return True
+
+ return False
+
+
+def sync(state, client, path, payload, check_mode, compare=do_differ):
+ remote_object = get(client, path)
+
+ if state == "absent" and remote_object is None:
+ return False, None
+
+ if state == "absent":
+ if not check_mode:
+ delete(client, path)
+ return True, None
+
+ # Making sure remote_object is present from here on
+
+ if remote_object is None or compare(remote_object, payload):
+ if check_mode:
+ return True, payload
+ put(client, path, payload)
+ return True, get(client, path)
+
+ return False, remote_object
+
+
+def sync_v1(state, client, path, payload, check_mode, compare=do_differ_v1):
+ changed, result = sync(state, client, path, payload, check_mode, compare)
+ return changed, convert_v1_to_v2_response(result)
+
+
+def _abort(msg, *args, **kwargs):
+ raise errors.SyncError(msg.format(*args, **kwargs))
+
+
+def get(client, path):
+ resp = client.get(path)
+ if resp.status not in (200, 404):
+ _abort(
+ "GET {0} failed with status {1}: {2}", path, resp.status, resp.data,
+ )
+ if resp.status == 200 and resp.json is None:
+ _abort("Server returned invalid JSON {0}", resp.data)
+ return resp.json
+
+
+def delete(client, path):
+ resp = client.delete(path)
+ if resp.status != 204:
+ _abort(
+ "DELETE {0} failed with status {1}: {2}",
+ path, resp.status, resp.data,
+ )
+ return None
+
+
+def put(client, path, payload):
+ resp = client.put(path, payload)
+ if resp.status not in (200, 201):
+ _abort(
+ "PUT {0} failed with status {1}: {2}",
+ path, resp.status, resp.data,
+ )
+ return None
+
+
+def dict_to_single_item_dicts(data):
+ return [{k: v} for k, v in data.items()]
+
+
+def single_item_dicts_to_dict(data):
+ result = {}
+ for item in data:
+ (k, v), = item.items()
+ result[k] = v
+ return result
+
+
+def dict_to_key_value_strings(data):
+ return ["{0}={1}".format(k, v) for k, v in data.items()]
+
+
+def build_url_path(api_group, api_version, namespace, *parts):
+ prefix = "/api/{0}/{1}/".format(api_group, api_version)
+ if namespace:
+ prefix += "namespaces/{0}/".format(quote(namespace, safe=""))
+ return prefix + "/".join(quote(p, safe="") for p in parts if p)
+
+
+def build_core_v2_path(namespace, *parts):
+ return build_url_path("core", "v2", namespace, *parts)
+
+
+def prepare_result_list(result):
+ if isinstance(result, list):
+ return result
+ return [] if result is None else [result]
+
+
+def convert_v1_to_v2_response(response):
+ # dict(metadata=<meta>, spec=dict(a=1)) -> dict(metadata=<meta>, a=1)
+
+ if not response:
+ return response
+
+ if "metadata" not in response:
+ return response["spec"]
+
+ # Move metadata key into the spec.
+ return dict(response["spec"], metadata=response["metadata"])
+
+
+def do_secrets_differ(current, desired):
+ return set(
+ (c["name"], c["secret"]) for c in (current.get("secrets") or [])
+ ) != set(
+ (d["name"], d["secret"]) for d in (desired.get("secrets") or [])
+ )
+
+
+def deprecate(module, msg, version):
+ try:
+ module.deprecate(msg, version=version, collection_name="sensu.sensu_go")
+ except TypeError:
+ # Ansible < 2.9.10 does not support collection_name kwarg. Output at
+ # least msg and version of collection when things will stop working.
+ module.deprecate(msg, version=version)
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/ad_auth_provider.py b/ansible_collections/sensu/sensu_go/plugins/modules/ad_auth_provider.py
new file mode 100644
index 00000000..609b16cb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/ad_auth_provider.py
@@ -0,0 +1,396 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: ad_auth_provider
+
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+
+short_description: Manage Sensu AD authentication provider
+
+description:
+ - Create, update or delete a Sensu Go AD authentication provider.
+ - For more information, refer to the Sensu Go documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/control-access/ad-auth/).
+
+version_added: 1.10.0
+
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+
+options:
+ servers:
+ description:
+ - An array of AD servers for your directory.
+ type: list
+ elements: dict
+ suboptions:
+ host:
+ description:
+ - AD server IP address.
+ required: true
+ type: str
+ port:
+ description:
+ - AD server port.
+ type: int
+ insecure:
+ description:
+ - Skips SSL certificate verification when set to true.
+ type: bool
+ default: false
+ security:
+ description:
+ - Encryption type to be used for the connection to the AD server.
+ type: str
+ choices: [ insecure, tls, starttls ]
+ default: tls
+ trusted_ca_file:
+ description:
+ - Path to an alternative CA bundle file.
+ type: str
+ client_cert_file:
+ description:
+ - Path to the certificate that should be sent to the server if requested.
+ type: str
+ client_key_file:
+ description:
+ - Path to the key file associated with the client_cert_file.
+ - Required if I(client_cert_file) is present.
+ type: str
+ default_upn_domain:
+ description:
+ - Enables UPN authentication when set. The default UPN suffix that will be appended
+ to the username when a domain is not specified during login
+ (for example, user becomes user@defaultdomain.xyz).
+ type: str
+ include_nested_groups:
+ description:
+ - If true, the group search includes any nested groups a user is a member of.
+ If false, the group search includes only the top-level groups a user is a member of.
+ type: bool
+ binding:
+ description:
+ - The AD account that performs user and group lookups.
+ - If your sever supports anonymous binding, you can omit the user_dn or password
+ attributes to query the directory without credentials.
+ type: dict
+ suboptions:
+ user_dn:
+ description:
+ - The AD account that performs user and group lookups.
+ - If your sever supports anonymous binding, you can omit this attribute.
+ type: str
+ required: true
+ password:
+ description:
+ - Password for the user_dn account.
+ - If your sever supports anonymous binding, you can omit this attribute.
+ type: str
+ required: true
+ group_search:
+ description:
+ - Search configuration for groups.
+ type: dict
+ suboptions:
+ base_dn:
+ description:
+ - Which part of the directory tree to search.
+ required: true
+ type: str
+ attribute:
+ description:
+ - Used for comparing result entries.
+ type: str
+ default: member
+ name_attribute:
+ description:
+ - Represents the attribute to use as the entry name.
+ type: str
+ default: cn
+ object_class:
+ description:
+ - Identifies the class of objects returned in the search result.
+ type: str
+ default: group
+ user_search:
+ description:
+ - Search configuration for users.
+ type: dict
+ suboptions:
+ base_dn:
+ description:
+ - Which part of the directory tree to search.
+ required: true
+ type: str
+ attribute:
+ description:
+ - Used for comparing result entries.
+ type: str
+ default: sAMAccountName
+ name_attribute:
+ description:
+ - Represents the attribute to use as the entry name.
+ type: str
+ default: displayName
+ object_class:
+ description:
+ - Identifies the class of objects returned in the search result.
+ type: str
+ default: person
+ groups_prefix:
+ description:
+ - The prefix added to all AD groups.
+ type: str
+ username_prefix:
+ description:
+ - The prefix added to all AD usernames.
+ type: str
+
+seealso:
+ - module: sensu.sensu_go.auth_provider_info
+ - module: sensu.sensu_go.ldap_auth_provider
+ - module: sensu.sensu_go.oidc_auth_provider
+"""
+
+EXAMPLES = """
+- name: Create a AD auth provider
+ sensu.sensu_go.ad_auth_provider:
+ name: activedirectory
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+- name: Delete a AD auth provider
+ sensu.sensu_go.ad_auth_provider:
+ name: activedirectory
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu AD authentication provider.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: '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'
+ group_search:
+ base_dn: 'dc=acme,dc=org'
+ attribute: 'member'
+ name_attribute': 'cn'
+ object_class: 'group'
+ user_search:
+ base_dn: 'dc=acme,dc=org'
+ attribute: 'sAMAccountName'
+ name_attribute: 'displayName'
+ object_class: 'person'
+ groups_prefix: 'AD'
+ username_prefix: 'AD'
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "authentication/v2"
+
+
+def remove_item(result):
+ if result:
+ for server in result["servers"]:
+ if server["binding"] and "password" in server["binding"]:
+ del server["binding"]["password"]
+
+ return result
+
+
+def _filter(payload):
+ # Remove keys with None values from dict
+ return dict((k, v) for k, v in payload.items() if v is not None)
+
+
+def do_differ(current, desired):
+ if utils.do_differ_v1(current, desired, "servers"):
+ return True
+
+ if len(current["spec"]["servers"]) != len(desired["spec"]["servers"]):
+ return True
+
+ for c, d in zip(current["spec"]["servers"], desired["spec"]["servers"]):
+ if utils.do_differ(c, _filter(d)):
+ return True
+
+ return False
+
+
+def main():
+ required_if = [("state", "present", ["servers"])]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth",
+ "name",
+ "state",
+ ),
+ servers=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ host=dict(
+ type="str",
+ required=True,
+ ),
+ port=dict(
+ type="int",
+ ),
+ insecure=dict(
+ type="bool",
+ default=False,
+ ),
+ security=dict(
+ type="str",
+ choices=["insecure", "tls", "starttls"],
+ default="tls",
+ ),
+ trusted_ca_file=dict(
+ type="str",
+ ),
+ client_cert_file=dict(
+ type="str",
+ ),
+ client_key_file=dict(
+ type="str",
+ ),
+ default_upn_domain=dict(
+ type="str",
+ ),
+ include_nested_groups=dict(
+ type="bool",
+ ),
+ binding=dict(
+ type="dict",
+ options=dict(
+ user_dn=dict(
+ type="str",
+ required=True,
+ ),
+ password=dict(
+ type="str",
+ no_log=True,
+ required=True,
+ ),
+ ),
+ ),
+ group_search=dict(
+ type="dict",
+ options=dict(
+ base_dn=dict(
+ type="str",
+ required=True,
+ ),
+ attribute=dict(
+ type="str",
+ default="member",
+ ),
+ name_attribute=dict(
+ type="str",
+ default="cn",
+ ),
+ object_class=dict(type="str", default="group"),
+ ),
+ ),
+ user_search=dict(
+ type="dict",
+ options=dict(
+ base_dn=dict(
+ type="str",
+ required=True,
+ ),
+ attribute=dict(
+ type="str",
+ default="sAMAccountName",
+ ),
+ name_attribute=dict(
+ type="str",
+ default="displayName",
+ ),
+ object_class=dict(
+ type="str",
+ default="person",
+ ),
+ ),
+ ),
+ ),
+ ),
+ groups_prefix=dict(
+ type="str",
+ ),
+ username_prefix=dict(
+ type="str",
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "authproviders", module.params["name"]
+ )
+
+ payload = dict(
+ type="ad",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=arguments.get_spec_payload(
+ module.params, "servers", "groups_prefix", "username_prefix"
+ ),
+ )
+
+ try:
+ changed, ad_provider = utils.sync_v1(
+ module.params["state"], client, path, payload, module.check_mode, do_differ
+ )
+ module.exit_json(changed=changed, object=remove_item(ad_provider))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/asset.py b/ansible_collections/sensu/sensu_go/plugins/modules/asset.py
new file mode 100644
index 00000000..627bfabb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/asset.py
@@ -0,0 +1,213 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Cameron Hurst <cahurst@cisco.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: asset
+author:
+ - Cameron Hurst (@wakemaster39)
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu assets
+description:
+ - Create, update or delete Sensu Go asset.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/assets/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.asset_info
+ - module: sensu.sensu_go.bonsai_asset
+options:
+ builds:
+ description:
+ - A list of asset builds used to define multiple artefacts which
+ provide the named asset.
+ - Required if I(state) is C(present).
+ type: list
+ elements: dict
+ suboptions:
+ url:
+ description:
+ - The URL location of the asset.
+ type: str
+ required: yes
+ sha512:
+ description:
+ - The checksum of the asset.
+ type: str
+ required: yes
+ filters:
+ description:
+ - A set of Sensu query expressions used to determine if the asset
+ should be installed.
+ type: list
+ elements: str
+ headers:
+ description:
+ - Additional headers to send when retrieving the asset, e.g. for
+ authorization.
+ type: dict
+"""
+
+EXAMPLES = """
+- name: Create a multiple-build asset
+ sensu.sensu_go.asset:
+ name: sensu-plugins-cpu-checks
+ 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'
+ - 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'
+
+- name: Delete an asset
+ sensu.sensu_go.asset:
+ name: sensu-plugins-cpu-check
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu asset.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: check_script
+ namespace: default
+ builds:
+ - sha512: 4f926bf4328f...2c58ad9ab40c9e2edc31b288d066b195b21b
+ url: http://example.com/asset.tar.gz
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def validate_module_params(params):
+ if params["state"] == "present":
+ if not params['builds']:
+ return "builds must include at least one element"
+ return None
+
+
+def _build_set(builds):
+ return set((
+ b.get('sha512'),
+ b.get('url'),
+ frozenset((b.get('headers', {}) or {}).items()),
+ frozenset(b.get('filters', []) or []),
+ ) for b in builds)
+
+
+def _do_builds_differ(current, desired):
+ # Since Sensu Go 5.16, the web API returns builds: None if the asset
+ # in question is a deprecated, single-build asset.
+ if current is None:
+ return True
+
+ if len(current) != len(desired):
+ return True
+
+ return _build_set(current) != _build_set(desired)
+
+
+def do_differ(current, desired):
+ if _do_builds_differ(current['builds'], desired['builds']):
+ return True
+
+ return utils.do_differ(current, desired, 'builds')
+
+
+def build_api_payload(params):
+ payload = arguments.get_mutation_payload(params)
+ if params['state'] == 'present':
+ builds = [arguments.get_spec_payload(b, *b.keys()) for b in params['builds']]
+ payload["builds"] = builds
+ return payload
+
+
+def main():
+ required_if = [
+ ("state", "present", ["builds"])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "namespace", "state", "labels", "annotations",
+ ),
+ builds=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ url=dict(
+ required=True,
+ ),
+ sha512=dict(
+ required=True,
+ ),
+ filters=dict(
+ type="list",
+ elements="str",
+ ),
+ headers=dict(
+ type="dict",
+ ),
+ )
+ ),
+ ),
+ )
+
+ msg = validate_module_params(module.params)
+ if msg:
+ module.fail_json(msg=msg)
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "assets", module.params["name"],
+ )
+ payload = build_api_payload(module.params)
+
+ try:
+ changed, asset = utils.sync(
+ module.params["state"], client, path, payload, module.check_mode, do_differ
+ )
+ module.exit_json(changed=changed, object=asset)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/asset_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/asset_info.py
new file mode 100644
index 00000000..3c128dcb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/asset_info.py
@@ -0,0 +1,99 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: asset_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu assets
+description:
+ - Retrieve information about Sensu Go assets.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/assets/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.asset
+ - module: sensu.sensu_go.bonsai_asset
+"""
+
+EXAMPLES = """
+- name: List all Sensu assets
+ sensu.sensu_go.asset_info:
+ register: result
+
+- name: List the selected Sensu asset
+ sensu.sensu_go.asset_info:
+ name: my_asset
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.metadata.name }}"
+
+"""
+
+RETURN = """
+objects:
+ description: List of Sensu assets.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: check_script
+ namespace: default
+ builds:
+ - sha512: 4f926bf4328f...2c58ad9ab40c9e2edc31b288d066b195b21b
+ url: http://example.com/asset.tar.gz
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "assets", module.params["name"],
+ )
+
+ try:
+ assets = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=assets)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/auth_provider_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/auth_provider_info.py
new file mode 100644
index 00000000..6215af44
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/auth_provider_info.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: auth_provider_info
+
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+
+short_description: List Sensu authentication providers
+
+description:
+ - Retrieve information about Sensu Go authentication providers.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/control-access/).
+
+version_added: 1.10.0
+
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+
+seealso:
+ - module: sensu.sensu_go.ad_auth_provider
+ - module: sensu.sensu_go.ldap_auth_provider
+ - module: sensu.sensu_go.oidc_auth_provider
+"""
+
+EXAMPLES = """
+- name: List all Sensu authentication providers
+ sensu.sensu_go.auth_provider_info:
+ register: result
+
+- name: List the selected Sensu authentication provider
+ sensu.sensu_go.auth_provider_info:
+ name: my_auth_provider
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.metadata.name }}"
+"""
+
+RETURN = """
+objects:
+ description: List of Sensu authentication providers.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: 'openldap'
+ groups_prefix: ''
+ servers:
+ binding:
+ user_dn: 'cn=binder,dc=acme,dc=org'
+ client_cert_file: ''
+ client_key_file: ''
+ default_upn_domain: ''
+ group_search:
+ attribute: 'member'
+ base_dn: 'dc=acme,dc=org'
+ name_attribute: 'cn'
+ object_class: 'groupOfNames'
+ host: '127.0.0.1'
+ insecure: false
+ port: 636
+ security: 'tls'
+ trusted_ca_file: ''
+ user_search:
+ attribute: 'uid'
+ base_dn: 'dc=acme,dc=org'
+ name_attribute: 'cn'
+ object_class: 'person'
+ username_prefix: ''
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "authentication/v2"
+
+
+def remove_item(result):
+ for server in result.get("servers", []):
+ if server["binding"] and "password" in server["binding"]:
+ del server["binding"]["password"]
+
+ if "client_secret" in result:
+ del result["client_secret"]
+
+ return result
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP,
+ API_VERSION,
+ None,
+ "authproviders",
+ module.params["name"],
+ )
+
+ try:
+ providers = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ # We simulate the behavior of v2 API here and only return the spec.
+ module.exit_json(
+ changed=False,
+ objects=[remove_item(utils.convert_v1_to_v2_response(p)) for p in providers],
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/bonsai_asset.py b/ansible_collections/sensu/sensu_go/plugins/modules/bonsai_asset.py
new file mode 100644
index 00000000..417218ec
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/bonsai_asset.py
@@ -0,0 +1,131 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: bonsai_asset
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: Add Sensu assets from Bonsai
+description:
+ - Create or update a Sensu Go asset whose definition is available in the
+ Bonsai, the Sensu asset index.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/assets/)
+ and U(https://bonsai.sensu.io/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+options:
+ version:
+ description:
+ - Version number of the asset to install.
+ type: str
+ required: true
+ rename:
+ description:
+ - The name that will be used when adding the asset to Sensu.
+ - If not present, value of the I(name) parameter will be used.
+ type: str
+ on_remote:
+ description:
+ - If set to C(true), module will download asset defnition on remote host.
+ - If not set or set to C(false), ansible downloads asset definition
+ on control node.
+ type: bool
+ version_added: 1.13.0
+notes:
+ - I(labels) and I(annotations) values are merged with the values obtained
+ from Bonsai. Values passed-in as parameters take precedence over the
+ values obtained from Bonsai.
+ - To delete an asset, use regular M(sensu.sensu_go.asset) module.
+seealso:
+ - module: sensu.sensu_go.asset
+ - module: sensu.sensu_go.asset_info
+"""
+
+EXAMPLES = """
+- name: Make sure specific version of asset is installed
+ sensu.sensu_go.bonsai_asset:
+ name: sensu/monitoring-plugins
+ version: 2.2.0-1
+
+- name: Remove previously added asset
+ sensu.sensu_go.asset:
+ name: sensu/monitoring-plugins
+ state: absent
+
+- name: Store Bonsai asset under a different name
+ sensu.sensu_go.bonsai_asset:
+ name: sensu/monitoring-plugins
+ version: 2.2.0-1
+ rename: sensu-monitoring-2.2.0-1
+
+- name: Display asset info
+ sensu.sensu_go.asset_info:
+ name: sensu-monitoring-2.2.0-1 # value from rename field
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu asset.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: check_script
+ namespace: default
+ builds:
+ - sha512: 4f926bf4328f...2c58ad9ab40c9e2edc31b288d066b195b21b
+ url: http://example.com/asset.tar.gz
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import bonsai, errors
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ name=dict(
+ type="str",
+ required=True,
+ ),
+ version=dict(
+ type="str",
+ required=True,
+ ),
+ ),
+ )
+
+ try:
+ asset = bonsai.get_asset_parameters(
+ module.params["name"], module.params["version"],
+ )
+ module.exit_json(changed=False, asset=asset)
+ except errors.Error as e:
+ module.fail_json(changed=False, msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/check.py b/ansible_collections/sensu/sensu_go/plugins/modules/check.py
new file mode 100644
index 00000000..47f44693
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/check.py
@@ -0,0 +1,449 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: check
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu checks
+description:
+ - Create, update or delete Sensu Go check.
+ - For more information, refer to the Sensu Go documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/checks/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+ - sensu.sensu_go.secrets
+seealso:
+ - module: sensu.sensu_go.check_info
+options:
+ command:
+ description:
+ - Check command to run.
+ - Required if I(state) is C(present).
+ type: str
+ subscriptions:
+ description:
+ - List of subscriptions which receive check requests.
+ - Required if I(state) is C(present).
+ type: list
+ elements: str
+ handlers:
+ description:
+ - List of handlers which receive check results.
+ type: list
+ elements: str
+ interval:
+ description:
+ - Check request interval.
+ - Cannot be used when I(cron) option is used.
+ type: int
+ cron:
+ description:
+ - Schedule check requests using crontab syntax.
+ - Cannot be used when I(interval) option is used.
+ type: str
+ publish:
+ description:
+ - Enables or disables scheduled publication of check requests.
+ type: bool
+ timeout:
+ description:
+ - Check execution timeout.
+ type: int
+ ttl:
+ description:
+ - Amount of time after which a check result is considered stale.
+ type: int
+ stdin:
+ description:
+ - Enables writing of serialized JSON data to the check command's stdin.
+ - Only usable with checks written specifically for Sensu Go.
+ type: bool
+ low_flap_threshold:
+ description:
+ - Low flap threshold.
+ type: int
+ high_flap_threshold:
+ description:
+ - High flap threshold.
+ type: int
+ runtime_assets:
+ description:
+ - List of runtime assets required to run the check.
+ type: list
+ elements: str
+ check_hooks:
+ description:
+ - A mapping of response codes to hooks which will be run by the agent
+ when that code is returned.
+ - Note that the structure of this parameter is a bit different from the
+ one described at
+ U(https://docs.sensu.io/sensu-go/latest/reference/checks/#check-hooks-attribute).
+ - See check hooks example below for more information on exact mapping
+ structure.
+ type: dict
+ proxy_entity_name:
+ description:
+ - Entity name to associate this check with instead of the agent it ran on.
+ type: str
+ proxy_requests:
+ description:
+ - Allows you to assign the check to run for multiple entities according
+ to their entity_attributes.
+ type: dict
+ suboptions:
+ entity_attributes:
+ description:
+ - List of attribute checks for determining which proxy entities this check should be scheduled against.
+ type: list
+ elements: str
+ splay:
+ description:
+ - Enables or disables splaying of check request scheduling.
+ type: bool
+ splay_coverage:
+ description:
+ - Percentage of the C(interval) over which to splay checks.
+ type: int
+ output_metric_format:
+ description:
+ - Enable parsing of metrics in the specified format from this check's
+ output.
+ type: str
+ choices:
+ - graphite_plaintext
+ - influxdb_line
+ - nagios_perfdata
+ - opentsdb_line
+ output_metric_handlers:
+ description:
+ - List of handlers which receive check results. I'm not sure why this exists.
+ type: list
+ elements: str
+ round_robin:
+ description:
+ - An array of environment variables to use with command execution.
+ type: bool
+ env_vars:
+ description:
+ - A mapping of environment variable names and values to use with command execution.
+ type: dict
+'''
+
+EXAMPLES = '''
+- name: Check executing command every 30 seconds
+ sensu.sensu_go.check:
+ name: check
+ command: check-cpu.sh -w 75 -c 90
+ subscriptions:
+ - checks
+ interval: 30
+ publish: yes
+
+- name: Check executing command with cron scheduler
+ sensu.sensu_go.check:
+ name: check
+ command: check-cpu.sh -w 75 -c 90
+ subscriptions:
+ - systems
+ handlers:
+ - slack
+ cron: "* * * * *"
+ publish: yes
+
+- name: Ad-hoc scheduling
+ sensu.sensu_go.check:
+ name: check
+ command: check-cpu.sh -w 75 -c 90
+ subscriptions:
+ - systems
+ handlers:
+ - slack
+ interval: 60
+ publish: no
+
+- name: Report events under proxy entity name instead of agent entity
+ sensu.sensu_go.check:
+ name: check
+ command: http_check.sh https://sensu.io
+ subscriptions:
+ - proxy
+ handlers:
+ - slack
+ interval: 60
+ proxy_entity_name: sensu-site
+ round_robin: yes
+ publish: yes
+
+- name: Event that triggers hooks
+ sensu.sensu_go.check:
+ name: check
+ command: http_check.sh https://sensu.io
+ subscriptions: [ proxy ]
+ # The upstream JSON payload for the hooks below would look like this:
+ #
+ # "check_hooks": [
+ # {"0": ["passing-hook", "always-run-this-hook"]},
+ # {"critical": ["failing-hook", "always-run-this-hook"]}
+ # ]
+ #
+ # Ansible task simplifies this structure into a simple mapping:
+ check_hooks:
+ "0":
+ - passing-hook
+ - always-run-this-hook
+ critical:
+ - failing-hook
+ - always-run-this-hook
+
+- name: Remove check
+ sensu.sensu_go.check:
+ name: my-check
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu check.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: check_minimum
+ namespace: default
+ command: collect.sh
+ handlers:
+ - slack
+ interval: 10
+ publish: true
+ subscriptions:
+ - system
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def validate_module_params(module):
+ params = module.params
+ proxy_requests = params['proxy_requests']
+
+ if (proxy_requests and proxy_requests.get('splay', False) and
+ proxy_requests.get('splay_coverage') is None):
+ module.fail_json(msg='splay is true but all of the following are missing: splay_coverage')
+
+ if params['state'] == 'present' and not (params['interval'] or params['cron']):
+ module.fail_json(msg='one of the following is required: interval, cron')
+
+
+def do_sets_differ(current, desired, key):
+ return set(current.get(key) or []) != set(desired.get(key) or [])
+
+
+def do_proxy_requests_differ(current, desired):
+ if 'proxy_requests' not in desired:
+ return False
+
+ current = current.get('proxy_requests') or {}
+ desired = desired['proxy_requests']
+
+ return (
+ (
+ 'entity_attributes' in desired and
+ do_sets_differ(current, desired, 'entity_attributes')
+ ) or
+ utils.do_differ(current, desired, 'entity_attributes')
+ )
+
+
+def do_check_hooks_differ(current, desired):
+ if 'check_hooks' not in desired:
+ return False
+
+ current = utils.single_item_dicts_to_dict(current.get('check_hooks') or [])
+ current = dict((k, set(v)) for k, v in current.items())
+
+ desired = utils.single_item_dicts_to_dict(desired['check_hooks'])
+ desired = dict((k, set(v)) for k, v in desired.items())
+
+ return current != desired
+
+
+def do_differ(current, desired):
+ return (
+ utils.do_differ(
+ current, desired, 'proxy_requests', 'subscriptions', 'handlers',
+ 'runtime_assets', 'check_hooks', 'output_metric_handlers',
+ 'env_vars', 'secrets',
+ ) or
+ utils.do_secrets_differ(current, desired) or
+ do_proxy_requests_differ(current, desired) or
+ do_sets_differ(current, desired, 'subscriptions') or
+ do_sets_differ(current, desired, 'handlers') or
+ do_sets_differ(current, desired, 'runtime_assets') or
+ do_check_hooks_differ(current, desired) or
+ do_sets_differ(current, desired, 'output_metric_handlers') or
+ do_sets_differ(current, desired, 'env_vars')
+ )
+
+
+def build_api_payload(params):
+ payload = arguments.get_mutation_payload(
+ params,
+ 'command',
+ 'cron',
+ 'handlers',
+ 'high_flap_threshold',
+ 'interval',
+ 'low_flap_threshold',
+ 'output_metric_format',
+ 'output_metric_handlers',
+ 'proxy_entity_name',
+ 'publish',
+ 'round_robin',
+ 'runtime_assets',
+ 'secrets',
+ 'stdin',
+ 'subscriptions',
+ 'timeout',
+ 'ttl'
+ )
+
+ if params['proxy_requests']:
+ payload['proxy_requests'] = arguments.get_spec_payload(
+ params['proxy_requests'],
+ 'entity_attributes', 'splay', 'splay_coverage',
+ )
+
+ if params['check_hooks']:
+ payload['check_hooks'] = utils.dict_to_single_item_dicts(params['check_hooks'])
+
+ if params['env_vars']:
+ payload['env_vars'] = utils.dict_to_key_value_strings(params['env_vars'])
+
+ return payload
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['subscriptions', 'command'])
+ ]
+ mutually_exclusive = [('interval', 'cron')]
+
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ "secrets",
+ ),
+ command=dict(),
+ subscriptions=dict(
+ type='list', elements='str',
+ ),
+ handlers=dict(
+ type='list', elements='str',
+ ),
+ interval=dict(
+ type='int'
+ ),
+ cron=dict(),
+ publish=dict(
+ type='bool'
+ ),
+ timeout=dict(
+ type='int'
+ ),
+ ttl=dict(
+ type='int'
+ ),
+ stdin=dict(
+ type='bool'
+ ),
+ env_vars=dict(
+ type='dict'
+ ),
+ low_flap_threshold=dict(
+ type='int'
+ ),
+ high_flap_threshold=dict(
+ type='int'
+ ),
+ runtime_assets=dict(
+ type='list', elements='str',
+ ),
+ check_hooks=dict(
+ type='dict'
+ ),
+ proxy_entity_name=dict(),
+ proxy_requests=dict(
+ type='dict',
+ options=dict(
+ entity_attributes=dict(
+ type='list', elements='str',
+ ),
+ splay=dict(
+ type='bool'
+ ),
+ splay_coverage=dict(
+ type='int'
+ )
+ )
+ ),
+ output_metric_format=dict(
+ choices=['nagios_perfdata', 'graphite_plaintext', 'influxdb_line', 'opentsdb_line']
+ ),
+ output_metric_handlers=dict(
+ type='list', elements='str',
+ ),
+ round_robin=dict(
+ type='bool'
+ )
+ )
+ )
+ validate_module_params(module)
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'checks', module.params['name'],
+ )
+ payload = build_api_payload(module.params)
+
+ try:
+ changed, check = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ do_differ,
+ )
+ module.exit_json(changed=changed, object=check)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/check_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/check_info.py
new file mode 100644
index 00000000..3bb207c9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/check_info.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: check_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu checks
+description:
+ - Retrieve information about Sensu Go checks.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/checks/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.check
+'''
+
+EXAMPLES = '''
+- name: List all Sensu checks
+ sensu.sensu_go.check_info:
+ register: result
+
+- name: Obtain a specific check
+ sensu.sensu_go.check_info:
+ name: my-check
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu checks.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: check_minimum
+ namespace: default
+ command: collect.sh
+ handlers:
+ - slack
+ interval: 10
+ publish: true
+ subscriptions:
+ - system
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "checks", module.params["name"],
+ )
+
+ try:
+ checks = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=checks)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/cluster.py b/ansible_collections/sensu/sensu_go/plugins/modules/cluster.py
new file mode 100644
index 00000000..a75fbbe2
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/cluster.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2021, 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: cluster
+author:
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu Go clusters
+description:
+ - Create, update or delete Sensu cluster.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/deploy-sensu/cluster-sensu/).
+version_added: 1.9.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.cluster_info
+options:
+ api_urls:
+ description:
+ - List of API urls that compose a single cluster.
+ - Required if I(state) is C(present).
+ type: list
+ elements: str
+"""
+
+EXAMPLES = """
+- name: Create a small cluster
+ sensu.sensu_go.cluster:
+ name: small-cluster
+ api_urls: https://sensu.alpha.example.com:8080
+
+- name: Create a larger cluster
+ sensu.sensu_go.cluster:
+ name: large-cluster
+ api_urls:
+ - https://sensu.alpha.example.com:8080
+ - https://sensu.beta.example.com:8080
+ - https://sensu.gamma.example.com:8080
+
+- name: Delete a cluster
+ sensu.sensu_go.cluster:
+ name: small-cluster
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu cluster.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: alpha-cluster
+ api_urls:
+ - "http://10.10.0.1:8080"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "federation/v1"
+
+
+def main():
+ required_if = [
+ ("state", "present", ["api_urls"]),
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state"),
+ api_urls=dict(type="list", elements="str"),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "clusters", module.params["name"],
+ )
+
+ payload = dict(
+ type="Cluster",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=arguments.get_spec_payload(module.params, "api_urls"),
+ )
+ try:
+ changed, cluster = utils.sync_v1(
+ module.params["state"], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=cluster)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/cluster_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_info.py
new file mode 100644
index 00000000..0fb64c27
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_info.py
@@ -0,0 +1,101 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2021, 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: cluster_info
+author:
+ - Tadej Borovsak (@tadeboro)
+short_description: List available Sensu Go clusters
+description:
+ - Retrieve information about Sensu Go clusters.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/deploy-sensu/cluster-sensu/).
+version_added: 1.9.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.cluster
+"""
+
+EXAMPLES = """
+- name: List all Sensu Go clusters
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+
+- name: Retrieve the selected Sensu Go cluster
+ sensu.sensu_go.etcd_replicator_info:
+ name: my-cluster
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.api_urls }}"
+"""
+
+RETURN = """
+objects:
+ description: List of Sensu Go etcd clusters.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: alpha-cluster
+ api_urls:
+ - "http://10.10.0.1:8080"
+ - metadata:
+ name: beta-cluster
+ api_urls:
+ - "https://10.20.0.1:8080"
+ - "https://10.20.0.2:8080"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "federation/v1"
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "clusters", module.params["name"],
+ )
+
+ try:
+ clusters = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ # We simulate the behavior of v2 API here and only return the spec.
+ module.exit_json(changed=False, objects=[
+ utils.convert_v1_to_v2_response(s) for s in clusters
+ ])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role.py b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role.py
new file mode 100644
index 00000000..20aecb54
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role.py
@@ -0,0 +1,164 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: cluster_role
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu cluster roles
+description:
+ - Create, update or delete Sensu role.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#roles-and-cluster-roles).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.cluster_role_info
+ - module: sensu.sensu_go.cluster_role_binding
+ - module: sensu.sensu_go.role
+ - module: sensu.sensu_go.role_binding
+options:
+ rules:
+ description:
+ - Rules that the cluster role applies.
+ - Must be non-empty if I(state) is C(present).
+ type: list
+ elements: dict
+ suboptions:
+ verbs:
+ description:
+ - Permissions to be applied by the rule.
+ type: list
+ elements: str
+ required: yes
+ choices: [get, list, create, update, delete]
+ resources:
+ description:
+ - Types of resources the rule has permission to access.
+ type: list
+ elements: str
+ required: yes
+ resource_names:
+ description:
+ - Names of specific resources the rule has permission to access.
+ - Note that for the C(create) verb, this argument will not be
+ taken into account when enforcing RBAC, even if it is provided.
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a cluster role
+ sensu.sensu_go.cluster_role:
+ name: readonly
+ rules:
+ - verbs:
+ - get
+ - list
+ resources:
+ - checks
+ - entities
+
+- name: Delete a cluster role
+ sensu.sensu_go.cluster_role:
+ name: readonly
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu cluster role.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: cluster-role
+ rules:
+ - resource_names:
+ - sample-name
+ resources:
+ - assets
+ - checks
+ verbs:
+ - get
+ - list
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils, role_utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state"),
+ rules=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ verbs=dict(
+ required=True,
+ type="list",
+ elements="str",
+ choices=["get", "list", "create", "update", "delete"],
+ ),
+ resources=dict(
+ required=True,
+ type="list",
+ elements="str",
+ ),
+ resource_names=dict(
+ type="list",
+ elements="str",
+ ),
+ )
+ )
+ )
+ )
+
+ msg = role_utils.validate_module_params(module.params)
+ if msg:
+ module.fail_json(msg=msg)
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ None, "clusterroles", module.params["name"],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, "rules"
+ )
+
+ try:
+ changed, cluster_role = utils.sync(
+ module.params['state'], client, path,
+ payload, module.check_mode, role_utils.do_roles_differ
+ )
+ module.exit_json(changed=changed, object=cluster_role)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding.py b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding.py
new file mode 100644
index 00000000..de60d025
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: cluster_role_binding
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu cluster role bindings
+description:
+ - Create, update or delete Sensu cluster role binding.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#role-bindings-and-cluster-role-bindings).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+options:
+ cluster_role:
+ description:
+ - Name of the cluster role.
+ - Required if I(state) is C(present).
+ type: str
+ users:
+ description:
+ - List of users to bind to the cluster role.
+ - Note that at least one of I(users) and I(groups) must be
+ specified when creating a cluster role binding.
+ type: list
+ elements: str
+ groups:
+ description:
+ - List of groups to bind to the cluster role.
+ - Note that at least one of I(users) and I(groups) must be
+ specified when creating a cluster role binding.
+ type: list
+ elements: str
+seealso:
+ - module: sensu.sensu_go.cluster_role_binding_info
+ - module: sensu.sensu_go.cluster_role
+ - module: sensu.sensu_go.role_binding
+'''
+
+EXAMPLES = '''
+- name: Create a cluster role binding
+ sensu.sensu_go.cluster_role_binding:
+ name: all-cluster-admins
+ cluster_role: cluster-admin
+ groups:
+ - cluster-admins
+ users:
+ - alice
+
+- name: Delete a cluster role binding
+ sensu.sensu_go.cluster_role_binding:
+ name: all-cluster-admins
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu cluster role binding.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: cluster-admin
+ role_ref:
+ name: cluster-admin
+ type: ClusterRole
+ subjects:
+ - name: cluster-admins
+ type: Group
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils, role_utils
+
+
+def build_api_payload(params):
+ payload = arguments.get_mutation_payload(params)
+ payload["subjects"] = role_utils.build_subjects(params["groups"], params["users"])
+ payload["role_ref"] = role_utils.type_name_dict("ClusterRole", params["cluster_role"])
+
+ return payload
+
+
+def main():
+ required_if = [
+ ("state", "present", ["cluster_role"])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state"),
+ cluster_role=dict(),
+ users=dict(
+ type="list", elements="str",
+ ),
+ groups=dict(
+ type="list", elements="str",
+ ),
+ )
+ )
+
+ msg = role_utils.validate_binding_module_params(module.params)
+ if msg:
+ module.fail_json(msg=msg)
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ None, "clusterrolebindings", module.params["name"],
+ )
+ payload = build_api_payload(module.params)
+
+ try:
+ changed, cluster_role_binding = utils.sync(
+ module.params["state"], client, path, payload, module.check_mode, role_utils.do_role_bindings_differ
+ )
+ module.exit_json(changed=changed, object=cluster_role_binding)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding_info.py
new file mode 100644
index 00000000..8d46fdc9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_binding_info.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: cluster_role_binding_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu cluster role bindings
+description:
+ - Retrieve information about Sensu cluster role bindings.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#role-bindings-and-cluster-role-bindings).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.cluster_role_binding
+'''
+
+EXAMPLES = '''
+- name: List all Sensu cluster role bindings
+ sensu.sensu_go.cluster_role_binding_info:
+ register: result
+
+- name: Retrieve a specific Sensu cluster role binding
+ sensu.sensu_go.cluster_role_binding_info:
+ name: my-binding
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu cluster role bindings.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: cluster-admin
+ role_ref:
+ name: cluster-admin
+ type: ClusterRole
+ subjects:
+ - name: cluster-admins
+ type: Group
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict()
+ )
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ None, "clusterrolebindings", module.params["name"],
+ )
+
+ try:
+ cluster_role_bindings = utils.prepare_result_list(
+ utils.get(client, path)
+ )
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=cluster_role_bindings)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_info.py
new file mode 100644
index 00000000..e2374dff
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/cluster_role_info.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: cluster_role_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu cluster roles
+description:
+ - Retrieve information about Sensu roles.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#roles-and-cluster-roles).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.cluster_role
+'''
+
+EXAMPLES = '''
+- name: List all Sensu cluster roles
+ sensu.sensu_go.cluster_role_info:
+ register: result
+
+- name: Retrieve Sensu cluster role by name
+ sensu.sensu_go.cluster_role_info:
+ name: my-custer-role
+ register: result
+'''
+
+RETURN = '''
+roles:
+ description: List of Sensu cluster roles.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: cluster-role
+ rules:
+ - resource_names:
+ - sample-name
+ resources:
+ - assets
+ - checks
+ verbs:
+ - get
+ - list
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict()
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ None, "clusterroles", module.params["name"],
+ )
+
+ try:
+ cluster_roles = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=cluster_roles)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/datastore.py b/ansible_collections/sensu/sensu_go/plugins/modules/datastore.py
new file mode 100644
index 00000000..706f3d1d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/datastore.py
@@ -0,0 +1,170 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: datastore
+author:
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu external datastore providers
+description:
+ - Add or remove external datastore provider.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/datastore/).
+version_added: 1.1.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.datastore_info
+options:
+ dsn:
+ description:
+ - Attribute that specifies the data source names as a URL or
+ PostgreSQL connection string. See the PostgreSQL docs for more
+ information about connection strings.
+ type: str
+ pool_size:
+ description:
+ - The maximum number of connections to hold in the PostgreSQL connection
+ pool.
+ type: int
+notes:
+ - Currently, only one external datastore can be active at a time. The module
+ will fail to perform its operation if this would break that invariant.
+'''
+
+EXAMPLES = '''
+- name: Add external datastore
+ sensu.sensu_go.datastore:
+ name: my-postgres
+ dsn: postgresql://user:secret@host:port/dbname
+
+- name: Remove external datastore
+ sensu.sensu_go.datastore:
+ name: my-postgres
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing external datastore provider.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: my-postgres
+ batch_buffer: 0
+ batch_size: 1
+ batch_workers: 0
+ dsn: "postgresql://user:secret@host:port/dbname"
+ max_conn_lifetime: 5m
+ max_idle_conns: 2
+ pool_size: 20
+ strict: true
+ enable_round_robin: true
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "store/v1"
+
+
+def _get(client, path):
+ return utils.convert_v1_to_v2_response(utils.get(client, path))
+
+
+def sync(state, client, list_path, resource_path, payload, check_mode):
+ datastore = _get(client, resource_path)
+
+ # When we are deleting stores, we do not care if there is more than one
+ # datastore present. We just make sure the currently manipulated store is
+ # gone. This makes our module useful in "let us clean up the mess"
+ # scenarios.
+ if state == "absent" and datastore is None:
+ return False, None
+
+ if state == "absent":
+ if not check_mode:
+ utils.delete(client, resource_path)
+ return True, None
+
+ # If the store exists, update it and ignore the fact that there might be
+ # more than one present.
+ if datastore:
+ if utils.do_differ(datastore, payload["spec"]):
+ if check_mode:
+ return True, payload["spec"]
+ utils.put(client, resource_path, payload)
+ return True, _get(client, resource_path)
+ return False, datastore
+
+ # When adding a new datastore, we first make sure there is no other
+ # datastore present because we do not want to be the ones who brought
+ # backends into an inconsistent state.
+ if utils.get(client, list_path):
+ raise errors.Error("Some other external datastore is already active.")
+
+ if check_mode:
+ return True, payload["spec"]
+ utils.put(client, resource_path, payload)
+ return True, _get(client, resource_path)
+
+
+def main():
+ required_if = [
+ ("state", "present", ["dsn"])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state"),
+ dsn=dict(),
+ pool_size=dict(
+ type="int",
+ )
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ list_path = utils.build_url_path(API_GROUP, API_VERSION, None, "provider")
+ resource_path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "provider", module.params["name"],
+ )
+ payload = dict(
+ type="PostgresConfig",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=arguments.get_spec_payload(module.params, "dsn", "pool_size"),
+ )
+
+ try:
+ changed, datastore = sync(
+ module.params["state"], client, list_path, resource_path, payload,
+ module.check_mode,
+ )
+ module.exit_json(changed=changed, object=datastore)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/datastore_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/datastore_info.py
new file mode 100644
index 00000000..b31c6481
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/datastore_info.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: datastore_info
+author:
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: List external Sensu datastore providers
+description:
+ - Retrieve information about external Sensu datastores.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/datastore/).
+version_added: 1.1.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.datastore
+"""
+
+EXAMPLES = """
+- name: List all external Sensu datastores
+ sensu.sensu_go.datastore_info:
+ register: result
+
+- name: Retrieve the selected external Sensu datastore
+ sensu.sensu_go.datastore_info:
+ name: my-datastore
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.dsn }}"
+"""
+
+RETURN = """
+objects:
+ description: List of external Sensu datastore providers.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: my-postgres
+ batch_buffer: 0
+ batch_size: 1
+ batch_workers: 0
+ dsn: "postgresql://user:secret@host:port/dbname"
+ max_conn_lifetime: 5m
+ max_idle_conns: 2
+ pool_size: 20
+ strict: true
+ enable_round_robin: true
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "store/v1"
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "provider", module.params["name"],
+ )
+
+ try:
+ stores = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ # We simulate the behavior of v2 API here and only return the spec.
+ module.exit_json(changed=False, objects=[
+ utils.convert_v1_to_v2_response(s) for s in stores
+ ])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/entity.py b/ansible_collections/sensu/sensu_go/plugins/modules/entity.py
new file mode 100644
index 00000000..e8d1562d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/entity.py
@@ -0,0 +1,261 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: entity
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu entities
+description:
+ - Create, update or delete Sensu entity.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/entities/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.entity_info
+options:
+ entity_class:
+ description:
+ - Entity class. Standard classes are C(proxy) and C(agent), but you can
+ use whatever you want.
+ - Required if I(state) is C(present).
+ type: str
+ subscriptions:
+ description:
+ - List of subscriptions for the entity.
+ type: list
+ elements: str
+ system:
+ description:
+ - System information about the entity, such as operating system and platform. See
+ U(https://docs.sensu.io/sensu-go/5.13/reference/entities/#system-attributes) for more information.
+ type: dict
+ last_seen:
+ description:
+ - Timestamp the entity was last seen, in seconds since the Unix epoch.
+ type: int
+ deregister:
+ description:
+ - If the entity should be removed when it stops sending keepalive messages.
+ type: bool
+ deregistration_handler:
+ description:
+ - The name of the handler to be called when an entity is deregistered.
+ type: str
+ redact:
+ description:
+ - List of items to redact from log messages. If a value is provided,
+ it overwrites the default list of items to be redacted.
+ type: list
+ elements: str
+ user:
+ description:
+ - Sensu RBAC username used by the entity. Agent entities require get,
+ list, create, update, and delete permissions for events across all namespaces.
+ type: str
+'''
+
+EXAMPLES = '''
+- name: Create an entity
+ sensu.sensu_go.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
+
+- name: Delete an entity
+ sensu.sensu_go.entity:
+ name: entity
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu entity.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ annotations: null
+ labels: null
+ name: webserver01
+ namespace: default
+ deregister: false
+ deregistration: {}
+ entity_class: agent
+ last_seen: 1542667231
+ redact:
+ - password
+ - private_key
+ - secret
+ subscriptions:
+ - entity:webserver01
+ system:
+ arch: amd64
+ libc_type: glibc
+ vm_system: kvm
+ vm_role: host
+ cloud_provider: null
+ network:
+ interfaces:
+ - addresses:
+ - 127.0.0.1/8
+ - ::1/128
+ name: lo
+ - addresses:
+ - 172.28.128.3/24
+ - fe80::a00:27ff:febc:be60/64
+ mac: 08:00:27:bc:be:60
+ name: enp0s8
+ os: linux
+ platform: centos
+ platform_family: rhel
+ platform_version: 7.4.1708
+ sensu_agent_version: 1.0.0
+ user: agent
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def do_differ(current, desired):
+ system = desired.get('system')
+ if system and utils.do_differ(current.get('system'), system):
+ return True
+
+ subs = desired.get('subscriptions')
+ if subs is not None and set(subs) != set(current.get('subscriptions', [])):
+ return True
+
+ return utils.do_differ(current, desired, 'system', 'subscriptions')
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['entity_class'])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ ),
+ entity_class=dict(),
+ subscriptions=dict(
+ type='list', elements='str',
+ ),
+ system=dict(
+ type='dict'
+ ),
+ last_seen=dict(
+ type='int'
+ ),
+ deregister=dict(
+ type='bool'
+ ),
+ deregistration_handler=dict(),
+ redact=dict(
+ type='list', elements='str',
+ ),
+ user=dict()
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'entities', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'entity_class', 'subscriptions', 'system', 'last_seen', 'deregister',
+ 'redact', 'user'
+ )
+ if module.params['deregistration_handler']:
+ payload['deregistration'] = dict(handler=module.params['deregistration_handler'])
+
+ # As per conversation with @echlebek, the only two supported entity
+ # classes are agent and proxy. All other classes can lead to undefined
+ # behavior and should not be used.
+ eclass = payload.get('entity_class')
+ if eclass and eclass not in ('agent', 'proxy'):
+ deprecation_msg = (
+ 'The `entity_class` parameter should be set to either `agent` or '
+ '`proxy`. All other values can result in undefined behavior of '
+ 'the Sensu Go backend.'
+ )
+ utils.deprecate(module, deprecation_msg, '2.0.0')
+
+ # Agent entities always have entity:{entity_name} subscription enabled
+ # even if we pass an empty subscriptions. In order to prevent falsely
+ # reporting changed: true, we always add this subscription to the agent
+ # entities.
+ if eclass == 'agent':
+ entity_sub = 'entity:' + module.params['name']
+ subs = payload.get('subscriptions', [])
+ if entity_sub not in subs:
+ # Copy subs in order to avoid mutating module params
+ payload['subscriptions'] = subs + [entity_sub]
+
+ try:
+ changed, entity = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ do_differ,
+ )
+ module.exit_json(changed=changed, object=entity)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/entity_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/entity_info.py
new file mode 100644
index 00000000..511dd237
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/entity_info.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: entity_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu entities
+description:
+ - Retrieve information about Sensu entities.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/entities/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.entity
+'''
+
+EXAMPLES = '''
+- name: List all Sensu entities
+ sensu.sensu_go.entity_info:
+ register: result
+
+- name: Retrieve a specific Sensu entity
+ sensu.sensu_go.entity_info:
+ name: my-entity
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu entities.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ annotations: null
+ labels: null
+ name: webserver01
+ namespace: default
+ deregister: false
+ deregistration: {}
+ entity_class: agent
+ last_seen: 1542667231
+ redact:
+ - password
+ - private_key
+ - secret
+ subscriptions:
+ - entity:webserver01
+ system:
+ arch: amd64
+ libc_type: glibc
+ vm_system: kvm
+ vm_role: host
+ cloud_provider: null
+ network:
+ interfaces:
+ - addresses:
+ - 127.0.0.1/8
+ - ::1/128
+ name: lo
+ - addresses:
+ - 172.28.128.3/24
+ - fe80::a00:27ff:febc:be60/64
+ mac: 08:00:27:bc:be:60
+ name: enp0s8
+ os: linux
+ platform: centos
+ platform_family: rhel
+ platform_version: 7.4.1708
+ sensu_agent_version: 1.0.0
+ user: agent
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "entities", module.params["name"],
+ )
+
+ try:
+ entities = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=entities)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator.py b/ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator.py
new file mode 100644
index 00000000..143aca8f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator.py
@@ -0,0 +1,218 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2021, 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: etcd_replicator
+author:
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu Go etcd replicators
+description:
+ - Create, update or delete Sensu etcd replicator.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/deploy-sensu/etcdreplicators/).
+version_added: 1.9.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.etcd_replicator_info
+options:
+ ca_cert:
+ description:
+ - Path to an the PEM-format CA certificate to use for TLS client authentication.
+ - Required if I(insecure) is C(false).
+ type: str
+ cert:
+ description:
+ - Path to the PEM-format certificate to use for TLS client authentication. This
+ certificate is required for secure client communication.
+ - Required if I(insecure) is C(false).
+ type: str
+ key:
+ description:
+ - Path to the PEM-format key file associated with the cert to use for TLS client
+ authentication. This key and its corresponding certificate are required for
+ secure client communication.
+ - Required if I(insecure) is C(false).
+ type: str
+ insecure:
+ description:
+ - Disable transport security.
+ - Only set to C(true) in sandbox and experimental environments.
+ type: bool
+ default: false
+ url:
+ description:
+ - Destination cluster URLs.
+ - Required if I(state) is C(present).
+ type: list
+ elements: str
+ api_version:
+ description:
+ - Sensu API version of the resource to replicate.
+ type: str
+ resource:
+ description:
+ - Name of the resource to replicate.
+ - List of all resources is available at
+ U(https://docs.sensu.io/sensu-go/latest/operations/control-access/rbac/#resources).
+ - Required if I(state) is C(present).
+ type: str
+ namespace:
+ description:
+ - Namespace to constrain replication to.
+ - If you do not include namespace, all namespaces for a given resource are
+ replicated.
+ type: str
+ replication_interval:
+ description:
+ - Interval at which the resource will be replicated. In seconds.
+ type: int
+"""
+
+EXAMPLES = """
+- name: Create a minimal replicator
+ sensu.sensu_go.etcd_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
+
+- name: Create an insecure minimal replicator
+ sensu.sensu_go.etcd_replicator:
+ name: role_replicator
+ insecure: true
+ url:
+ - https://sensu.beta.example.com:2379
+ - https://sensu.gamma.example.com:2379
+ resource: Role
+
+- name: Create a replicator with all parameters set
+ sensu.sensu_go.etcd_replicator:
+ name: role_binding_replicator
+ ca_cert: /etc/sensu/certs/ca.pem
+ cert: /etc/sensu/certs/cert.pem
+ key: /etc/sensu/certs/key.pem
+ insecure: false
+ url: https://127.0.0.1:2379
+ api_version: core/v2
+ resource: RoleBinding
+ namespace: default
+ replication_interval_seconds: 30
+
+- name: Delete a replicator
+ sensu.sensu_go.etcd_replicator:
+ name: my_replicator
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu etcd replicator.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ created_by: admin
+ name: cluster-role-replicator
+ api_version: core/v2
+ ca_cert: /etc/sensu/certs/ca.pem
+ cert: /etc/sensu/certs/cert.pem
+ insecure: false
+ key: /etc/sensu/certs/key.pem
+ namespace: ""
+ replication_interval_seconds: 30
+ resource: ClusterRole
+ url: https://sensu.alpha.example.com:2379
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "federation/v1"
+
+
+def main():
+ required_if = [
+ ("state", "present", ["url", "resource"]),
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state"),
+ ca_cert=dict(type="str"),
+ cert=dict(type="str"),
+ key=dict(type="str", no_log=False),
+ insecure=dict(type="bool", default=False),
+ url=dict(type="list", elements="str"),
+ api_version=dict(type="str"),
+ resource=dict(type="str"),
+ namespace=dict(type="str"),
+ replication_interval=dict(type="int"),
+ ),
+ )
+
+ # This complex condition cannot be expressed using built-in checks.
+ if module.params["state"] == "present" and module.params["insecure"] is False:
+ missing = []
+ for key in ("ca_cert", "cert", "key"):
+ if not module.params[key]:
+ missing.append(key)
+ if missing:
+ msg = "insecure is False but all of the following are missing: {0}".format(
+ ", ".join(missing)
+ )
+ module.fail_json(msg=msg)
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "etcd-replicators", module.params["name"],
+ )
+
+ spec = arguments.get_spec_payload(
+ module.params, "ca_cert", "cert", "key", "insecure", "api_version",
+ "resource", "namespace",
+ )
+ # We renamed the replication interval a bit.
+ if module.params["replication_interval"] is not None:
+ spec["replication_interval_seconds"] = module.params["replication_interval"]
+ # We accept a list of urls that we need to convert here
+ if module.params["url"]:
+ spec["url"] = ",".join(module.params["url"])
+
+ payload = dict(
+ type="EtcdReplicator",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=spec,
+ )
+ try:
+ changed, replicator = utils.sync_v1(
+ module.params["state"], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=replicator)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator_info.py
new file mode 100644
index 00000000..cf58d7c8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/etcd_replicator_info.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: etcd_replicator_info
+author:
+ - Tadej Borovsak (@tadeboro)
+short_description: List available Sensu Go etcd replicators
+description:
+ - Retrieve information about Sensu Go etcd replicators.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/deploy-sensu/etcdreplicators/).
+version_added: 1.9.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.etcd_replicator
+"""
+
+EXAMPLES = """
+- name: List all Sensu Go etcd replicators
+ sensu.sensu_go.etcd_replicator_info:
+ register: result
+
+- name: Retrieve the selected Sensu Go etcd replicator
+ sensu.sensu_go.etcd_replicator_info:
+ name: role_replicator
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.resource }}"
+"""
+
+RETURN = """
+objects:
+ description: List of Sensu Go etcd replicators.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ created_by: admin
+ name: cluster-role-replicator
+ api_version: core/v2
+ ca_cert: /etc/sensu/certs/ca.pem
+ cert: /etc/sensu/certs/cert.pem
+ insecure: false
+ key: /etc/sensu/certs/key.pem
+ namespace: ""
+ replication_interval_seconds: 30
+ resource: ClusterRole
+ url: https://sensu.alpha.example.com:2379
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "federation/v1"
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "etcd-replicators", module.params["name"],
+ )
+
+ try:
+ replicators = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ # We simulate the behavior of v2 API here and only return the spec.
+ module.exit_json(changed=False, objects=[
+ utils.convert_v1_to_v2_response(s) for s in replicators
+ ])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/event.py b/ansible_collections/sensu/sensu_go/plugins/modules/event.py
new file mode 100644
index 00000000..ae2ae333
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/event.py
@@ -0,0 +1,335 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: event
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu events
+description:
+ - Send a synthetic event to Sensu.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/events/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.event_info
+notes:
+ - Metric events bypass the store and are sent off to the event pipeline and corresponding event
+ handlers. Read more about this at
+ U(https://docs.sensu.io/sensu-go/latest/reference/events/#metric-only-events).
+options:
+ timestamp:
+ description:
+ - UNIX time at which the event occurred.
+ type: int
+ entity:
+ description:
+ - Name of the entity associated with this event. It must exist before event creation.
+ type: str
+ required: true
+ check:
+ description:
+ - Name of the check associated with this event. It must exist before event creation.
+ type: str
+ required: true
+ check_attributes:
+ type: dict
+ description:
+ - Additional check parameters. Find out more at
+ U(https://docs.sensu.io/sensu-go/latest/reference/events/#check-attributes).
+ suboptions:
+ duration:
+ description:
+ - Command execution time in seconds.
+ type: float
+ executed:
+ description:
+ - Time that the check request was executed.
+ type: int
+ history:
+ description:
+ - Check status history for the last 21 check executions.
+ type: list
+ elements: dict
+ issued:
+ description:
+ - Time that the check request was issued in seconds since the Unix epoch.
+ type: int
+ last_ok:
+ description:
+ - The last time that the check returned an OK status (0) in seconds since the Unix epoch.
+ type: int
+ output:
+ description:
+ - The output from the execution of the check command.
+ type: str
+ state:
+ description:
+ - The state of the check.
+ choices: [ "passing", "failing", "flapping" ]
+ type: str
+ status:
+ description:
+ - Exit status code produced by the check.
+ choices: [ "ok", "warning", "critical", "unknown" ]
+ type: str
+ total_state_change:
+ description:
+ - The total state change percentage for the check's history.
+ type: int
+ metric_attributes:
+ type: dict
+ description:
+ - Metric attributes. Find out more at
+ U(https://docs.sensu.io/sensu-go/latest/reference/events/#metric-attributes).
+ suboptions:
+ handlers:
+ description:
+ - An array of Sensu handlers to use for events created by the check.
+ Each array item must be a string.
+ type: list
+ elements: str
+ points:
+ description:
+ - Metric data points including a name, timestamp, value, and tags.
+ type: list
+ elements: dict
+'''
+
+EXAMPLES = '''
+- name: Create an event
+ sensu.sensu_go.event:
+ auth:
+ url: http://localhost:8080
+ entity: awesome_entity
+ check: awesome_check
+ check_attributes:
+ duration: 1.945
+ executed: 1522100915
+ history:
+ - executed: 1552505193
+ status: 1
+ 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:
+ - 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
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu event (deprecated).
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ namespace: default
+ check:
+ check_hooks: null
+ command: check-cpu.sh -w 75 -c 90
+ duration: 1.07055808
+ env_vars: null
+ executed: 1552594757
+ handlers: []
+ high_flap_threshold: 0
+ history:
+ - executed: 1552594757
+ status: 0
+ interval: 60
+ metadata:
+ name: check-cpu
+ namespace: default
+ occurrences: 1
+ occurrences_watermark: 1
+ output: CPU OK - Usage:3.96
+ subscriptions:
+ - linux
+ timeout: 0
+ total_state_change: 0
+ ttl: 0
+ entity:
+ deregister: false
+ deregistration: {}
+ entity_class: agent
+ last_seen: 1552594641
+ metadata:
+ name: sensu-centos
+ namespace: default
+ timestamp: 1552594758
+ id: 3a5948f3-6ffd-4ea2-a41e-334f4a72ca2f
+ sequence: 1
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+STATUS_MAP = {
+ 'ok': 0,
+ 'warning': 1,
+ 'critical': 2,
+ 'unknown': 3,
+}
+
+
+def get_check(client, namespace, check):
+ check_path = utils.build_core_v2_path(namespace, 'checks', check)
+ resp = client.get(check_path)
+ if resp.status != 200:
+ raise errors.SyncError("Check with name '{0}' does not exist on remote.".format(check))
+ return resp.json
+
+
+def get_entity(client, namespace, entity):
+ entity_path = utils.build_core_v2_path(namespace, 'entities', entity)
+ resp = client.get(entity_path)
+ if resp.status != 200:
+ raise errors.SyncError("Entity with name '{0}' does not exist on remote.".format(entity))
+ return resp.json
+
+
+def _update_payload_with_metric_attributes(payload, metric_attributes):
+ if not metric_attributes:
+ return
+
+ payload['metrics'] = arguments.get_spec_payload(metric_attributes, *metric_attributes.keys())
+
+
+def _update_payload_with_check_attributes(payload, check_attributes):
+ if not check_attributes:
+ return
+
+ if check_attributes['status']:
+ check_attributes['status'] = STATUS_MAP[check_attributes['status']]
+
+ filtered_attributes = arguments.get_spec_payload(check_attributes, *check_attributes.keys())
+ payload['check'].update(filtered_attributes)
+
+
+def _build_api_payload(client, params):
+ payload = arguments.get_spec_payload(params, 'timestamp')
+ payload['metadata'] = dict(
+ namespace=params['namespace']
+ )
+ payload['entity'] = get_entity(client, params['namespace'], params['entity'])
+ payload['check'] = get_check(client, params['namespace'], params['check'])
+
+ _update_payload_with_check_attributes(payload, params['check_attributes'])
+ _update_payload_with_metric_attributes(payload, params['metric_attributes'])
+ return payload
+
+
+def send_event(client, path, payload, check_mode):
+ if not check_mode:
+ utils.put(client, path, payload)
+ return True, payload
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ timestamp=dict(type='int'),
+ entity=dict(required=True),
+ check=dict(required=True),
+ check_attributes=dict(
+ type='dict',
+ options=dict(
+ duration=dict(
+ type='float'
+ ),
+ executed=dict(
+ type='int'
+ ),
+ history=dict(
+ type='list', elements='dict',
+ ),
+ issued=dict(
+ type='int'
+ ),
+ last_ok=dict(
+ type='int'
+ ),
+ output=dict(),
+ state=dict(
+ choices=['passing', 'failing', 'flapping']
+ ),
+ status=dict(
+ choices=['ok', 'warning', 'critical', 'unknown']
+ ),
+ total_state_change=dict(
+ type='int'
+ )
+ )
+ ),
+ metric_attributes=dict(
+ type='dict',
+ options=dict(
+ handlers=dict(
+ type='list',
+ elements='str'
+ ),
+ points=dict(
+ type='list',
+ elements='dict'
+ )
+ )
+ )
+ )
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'events', module.params['entity'],
+ module.params['check'],
+ )
+
+ try:
+ payload = _build_api_payload(client, module.params)
+ changed, event = send_event(client, path, payload, module.check_mode)
+ module.exit_json(changed=changed, object=event)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/event_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/event_info.py
new file mode 100644
index 00000000..90a4d84f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/event_info.py
@@ -0,0 +1,142 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: event_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu events
+description:
+ - Retrieve recent events that Sensu processed.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/events/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.event
+options:
+ check:
+ description:
+ - Limit results to a specific check.
+ - I(entity) must also be specified if this parameter is used.
+ type: str
+ entity:
+ description:
+ - Limit results to a specific entity.
+ type: str
+'''
+
+EXAMPLES = '''
+- name: List Sensu events
+ sensu.sensu_go.event_info:
+ register: result
+
+- name: List Sensu events for api.example.com
+ sensu.sensu_go.event_info:
+ entity: api.example.com
+ register: result
+
+- name: Filter events by check and entity
+ sensu.sensu_go.event_info:
+ entity: api.example.com
+ check: check-cpu
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu events.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ namespace: default
+ check:
+ check_hooks: null
+ command: check-cpu.sh -w 75 -c 90
+ duration: 1.07055808
+ env_vars: null
+ executed: 1552594757
+ handlers: []
+ high_flap_threshold: 0
+ history:
+ - executed: 1552594757
+ status: 0
+ interval: 60
+ metadata:
+ name: check-cpu
+ namespace: default
+ occurrences: 1
+ occurrences_watermark: 1
+ output: CPU OK - Usage:3.96
+ subscriptions:
+ - linux
+ timeout: 0
+ total_state_change: 0
+ ttl: 0
+ entity:
+ deregister: false
+ deregistration: {}
+ entity_class: agent
+ last_seen: 1552594641
+ metadata:
+ name: sensu-centos
+ namespace: default
+ timestamp: 1552594758
+ id: 3a5948f3-6ffd-4ea2-a41e-334f4a72ca2f
+ sequence: 1
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ required_by = {'check': ['entity']}
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_by=required_by,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ check=dict(),
+ entity=dict(),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'events', module.params['entity'],
+ module.params['check'],
+ )
+
+ try:
+ events = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=events)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/filter.py b/ansible_collections/sensu/sensu_go/plugins/modules/filter.py
new file mode 100644
index 00000000..e8023b43
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/filter.py
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: filter
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu filters
+description:
+ - Create, update or delete Sensu filter.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/filters/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.filter_info
+options:
+ action:
+ description:
+ - Action to take with the event if the filter expressions match.
+ - Required if I(state) is C(present).
+ type: str
+ choices: [ 'allow', 'deny' ]
+ expressions:
+ description:
+ - Filter expressions to be compared with event data.
+ - Required if I(state) is C(present).
+ type: list
+ elements: str
+ runtime_assets:
+ description:
+ - Assets to be applied to the filter's execution context.
+ JavaScript files in the lib directory of the asset will be evaluated.
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a filter
+ sensu.sensu_go.filter:
+ name: filter
+ action: deny
+ expressions:
+ - event.check.interval == 10
+ - event.check.occurrences == 1
+ runtime_assets: awesomeness
+
+- name: Create a production filter
+ sensu.sensu_go.filter:
+ name: filter
+ action: allow
+ expressions:
+ - event.entity.labels['environment'] == 'production'
+
+- name: Create a filter with JS expression
+ sensu.sensu_go.filter:
+ name: filter
+ action: deny
+ expressions:
+ - "_.reduce(event.check.history, function(memo, h) { return (memo || h.status != 0); })"
+ runtime_assets:
+ - underscore
+
+- name: Handling repeated events
+ sensu.sensu_go.filter:
+ name: filter_interval_60_hourly
+ action: allow
+ expressions:
+ - event.check.interval == 60
+ - event.check.occurrences == 1 || event.check.occurrences % 60 == 0
+
+- name: Delete a filter
+ sensu.sensu_go.filter:
+ name: filter_interval_60_hourly
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu filter.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: filter_minimum
+ namespace: default
+ action: allow
+ expressions:
+ - event.check.occurrences == 1
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['action', 'expressions'])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ ),
+ action=dict(choices=['allow', 'deny']),
+ expressions=dict(
+ type='list', elements='str',
+ ),
+ runtime_assets=dict(
+ type='list', elements='str',
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'filters', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'action', 'expressions', 'runtime_assets'
+ )
+ try:
+ changed, sensu_filter = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=sensu_filter)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/filter_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/filter_info.py
new file mode 100644
index 00000000..bdad1fa5
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/filter_info.py
@@ -0,0 +1,93 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: filter_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu info
+description:
+ - Retrieve information about Sensu filters.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/filters/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.filter
+'''
+
+EXAMPLES = '''
+- name: List all Sensu filters
+ sensu.sensu_go.filter_info:
+ register: result
+
+- name: Retrieve a specific Sensu filter
+ sensu.sensu_go.filter_info:
+ name: my-filter
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu filters.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: filter_minimum
+ namespace: default
+ action: allow
+ expressions:
+ - event.check.occurrences == 1
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "filters", module.params["name"],
+ )
+
+ try:
+ sensu_filters = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=sensu_filters)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/handler_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/handler_info.py
new file mode 100644
index 00000000..600de0a1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/handler_info.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: handler_info
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu handlers
+description:
+ - Retrieve information about Sensu handlers.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/handlers/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.socket_handler
+ - module: sensu.sensu_go.pipe_handler
+ - module: sensu.sensu_go.handler_set
+'''
+
+EXAMPLES = '''
+- name: List all Sensu handlers
+ sensu.sensu_go.handler_info:
+ register: result
+
+- name: Retrieve info for a specific Sensu handler
+ sensu.sensu_go.handler_info:
+ name: my-handler
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu handlers.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: tcp_udp_handler_minimum
+ namespace: default
+ socket:
+ host: 10.0.1.99
+ port: 4444
+ type: tcp
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "handlers", module.params["name"],
+ )
+
+ try:
+ handlers = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=handlers)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/handler_set.py b/ansible_collections/sensu/sensu_go/plugins/modules/handler_set.py
new file mode 100644
index 00000000..aedc9856
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/handler_set.py
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: handler_set
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu handler set
+description:
+ - Create, update or delete Sensu handler set.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/handlers/#handler-sets).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.socket_handler
+ - module: sensu.sensu_go.pipe_handler
+ - module: sensu.sensu_go.handler_info
+options:
+ handlers:
+ description:
+ - List of Sensu event handlers (names) to use for events using the handler set.
+ - Required if I(state) is C(present).
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a handler set
+ sensu.sensu_go.handler_set:
+ name: notify_all_the_things
+ handlers:
+ - slack
+ - tcp_handler
+ - udp_handler
+
+- name: Delete a handler set
+ sensu.sensu_go.handler_set:
+ name: notify_all_the_things
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu handler set.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: tcp_udp_handler_minimum
+ namespace: default
+ handlers:
+ - slack
+ type: set
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['handlers'])
+ ]
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_if=required_if,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ ),
+ handlers=dict(
+ type='list', elements='str',
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'handlers', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'handlers'
+ )
+ payload['type'] = 'set'
+
+ try:
+ changed, handler = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=handler)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/hook.py b/ansible_collections/sensu/sensu_go/plugins/modules/hook.py
new file mode 100644
index 00000000..b78f90da
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/hook.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: hook
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu hooks
+description:
+ - Create, update or delete Sensu hook.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/hooks/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.hook_info
+options:
+ command:
+ description:
+ - Command to run when the hook is triggered.
+ - Required if I(state) is C(present).
+ type: str
+ timeout:
+ description:
+ - The hook execution duration timeout in seconds (hard stop).
+ - Required if I(state) is C(present).
+ type: int
+ stdin:
+ description:
+ - Controls whether Sensu writes serialized JSON data to the process's stdin.
+ type: bool
+ runtime_assets:
+ description:
+ - List of runtime assets required to run the check.
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Rudimentary auto-remediation hook
+ sensu.sensu_go.hook:
+ auth:
+ url: http://localhost:8080
+ name: restart_nginx
+ command: sudo systemctl start nginx
+ timeout: 60
+ stdin: false
+
+- name: Capture the process tree
+ sensu.sensu_go.hook:
+ auth:
+ url: http://localhost:8080
+ name: process_tree
+ command: ps aux
+ timeout: 60
+ stdin: false
+
+- name: Delete a hook
+ sensu.sensu_go.hook:
+ name: process_tree
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu hook.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ annotations: null
+ labels: null
+ name: restart_nginx
+ namespace: default
+ command: sudo systemctl start nginx
+ stdin: false
+ timeout: 60
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['command', 'timeout'])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ ),
+ command=dict(),
+ timeout=dict(
+ type='int',
+ ),
+ stdin=dict(
+ type='bool'
+ ),
+ runtime_assets=dict(
+ type='list', elements='str',
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'hooks', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'command', 'timeout', 'stdin', 'runtime_assets'
+ )
+ try:
+ changed, hook = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=hook)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/hook_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/hook_info.py
new file mode 100644
index 00000000..f576a5e4
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/hook_info.py
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: hook_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu hooks
+description:
+ - Retrieve information about Sensu hooks.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/hooks/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.hook
+'''
+
+EXAMPLES = '''
+- name: List all Sensu hooks
+ sensu.sensu_go.hook_info:
+ register: result
+
+- name: Fetch a specific Sensu hook
+ sensu.sensu_go.hook_info:
+ name: awesome-hook
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu hooks.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ annotations: null
+ labels: null
+ name: restart_nginx
+ namespace: default
+ command: sudo systemctl start nginx
+ stdin: false
+ timeout: 60
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "hooks", module.params["name"],
+ )
+
+ try:
+ hooks = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=hooks)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/ldap_auth_provider.py b/ansible_collections/sensu/sensu_go/plugins/modules/ldap_auth_provider.py
new file mode 100644
index 00000000..e0ee4da0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/ldap_auth_provider.py
@@ -0,0 +1,378 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: ldap_auth_provider
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+
+short_description: Manage Sensu LDAP authentication provider
+
+description:
+ - Create, update or delete a Sensu Go LDAP authentication provider.
+ - For more information, refer to the Sensu Go documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/control-access/ldap-auth/).
+
+version_added: 1.10.0
+
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+
+options:
+ servers:
+ description:
+ - An array of LDAP servers for your directory.
+ - Required if I(state) is C(present).
+ type: list
+ elements: dict
+ suboptions:
+ host:
+ description:
+ - LDAP server IP address.
+ required: true
+ type: str
+ port:
+ description:
+ - LDAP server port.
+ type: int
+ insecure:
+ description:
+ - Skips SSL certificate verification when set to true.
+ type: bool
+ default: false
+ security:
+ description:
+ - Encryption type to be used for the connection to the LDAP server.
+ type: str
+ choices: [ insecure, tls, starttls ]
+ default: tls
+ trusted_ca_file:
+ description:
+ - Path to an alternative CA bundle file.
+ type: str
+ client_cert_file:
+ description:
+ - Path to the certificate that should be sent to the server if requested.
+ type: str
+ client_key_file:
+ description:
+ - Path to the key file associated with the client_cert_file.
+ - Required if I(client_cert_file) is present.
+ type: str
+ binding:
+ description:
+ - The LDAP account that performs user and group lookups.
+ - If your sever supports anonymous binding, you can omit the user_dn or password
+ attributes to query the directory without credentials.
+ type: dict
+ suboptions:
+ user_dn:
+ description:
+ - The LDAP account that performs user and group lookups.
+ - If your sever supports anonymous binding, you can omit this attribute.
+ type: str
+ required: true
+ password:
+ description:
+ - Password for the user_dn account.
+ - If your sever supports anonymous binding, you can omit this attribute.
+ type: str
+ required: true
+ group_search:
+ description:
+ - Search configuration for groups.
+ type: dict
+ suboptions:
+ base_dn:
+ description:
+ - Which part of the directory tree to search.
+ required: true
+ type: str
+ attribute:
+ description:
+ - Used for comparing result entries.
+ type: str
+ default: member
+ name_attribute:
+ description:
+ - Represents the attribute to use as the entry name.
+ type: str
+ default: cn
+ object_class:
+ description:
+ - Identifies the class of objects returned in the search result.
+ type: str
+ default: groupOfNames
+ user_search:
+ description:
+ - Search configuration for users.
+ type: dict
+ suboptions:
+ base_dn:
+ description:
+ - Which part of the directory tree to search.
+ required: true
+ type: str
+ attribute:
+ description:
+ - Used for comparing result entries.
+ type: str
+ default: uid
+ name_attribute:
+ description:
+ - Represents the attribute to use as the entry name.
+ type: str
+ default: cn
+ object_class:
+ description:
+ - Identifies the class of objects returned in the search result.
+ type: str
+ default: person
+ groups_prefix:
+ description:
+ - The prefix added to all LDAP groups.
+ type: str
+ username_prefix:
+ description:
+ - The prefix added to all LDAP usernames.
+ type: str
+
+seealso:
+ - module: sensu.sensu_go.auth_provider_info
+ - module: sensu.sensu_go.ad_auth_provider
+ - module: sensu.sensu_go.oidc_auth_provider
+"""
+
+EXAMPLES = """
+- name: Create a LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider:
+ name: openldap
+ servers:
+ - host: 127.0.0.1
+ group_search:
+ base_dn: dc=acme,dc=org
+ user_search:
+ base_dn: dc=acme,dc=org
+
+- name: Delete a LDAP auth provider
+ sensu.sensu_go.ldap_auth_provider:
+ name: openldap
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu LDAP authentication provider.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: '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'
+ 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: 'ldap'
+ username_prefix: 'ldap'
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "authentication/v2"
+
+
+def remove_item(result):
+ if result:
+ for server in result["servers"]:
+ if server["binding"] and "password" in server["binding"]:
+ del server["binding"]["password"]
+
+ return result
+
+
+def _filter(payload):
+ # Remove keys with None values from dict
+ return dict((k, v) for k, v in payload.items() if v is not None)
+
+
+def do_differ(current, desired):
+ if utils.do_differ_v1(current, desired, "servers"):
+ return True
+
+ if len(current["spec"]["servers"]) != len(desired["spec"]["servers"]):
+ return True
+
+ for c, d in zip(current["spec"]["servers"], desired["spec"]["servers"]):
+ if utils.do_differ(c, _filter(d)):
+ return True
+
+ return False
+
+
+def main():
+ required_if = [("state", "present", ["servers"])]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth",
+ "name",
+ "state",
+ ),
+ servers=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ host=dict(
+ type="str",
+ required=True,
+ ),
+ port=dict(
+ type="int",
+ ),
+ insecure=dict(
+ type="bool",
+ default=False,
+ ),
+ security=dict(
+ type="str",
+ choices=["insecure", "tls", "starttls"],
+ default="tls",
+ ),
+ trusted_ca_file=dict(
+ type="str",
+ ),
+ client_cert_file=dict(
+ type="str",
+ ),
+ client_key_file=dict(
+ type="str",
+ ),
+ binding=dict(
+ type="dict",
+ options=dict(
+ user_dn=dict(
+ type="str",
+ required=True,
+ ),
+ password=dict(
+ type="str",
+ no_log=True,
+ required=True,
+ ),
+ ),
+ ),
+ group_search=dict(
+ type="dict",
+ options=dict(
+ base_dn=dict(
+ type="str",
+ required=True,
+ ),
+ attribute=dict(
+ type="str",
+ default="member",
+ ),
+ name_attribute=dict(
+ type="str",
+ default="cn",
+ ),
+ object_class=dict(type="str", default="groupOfNames"),
+ ),
+ ),
+ user_search=dict(
+ type="dict",
+ options=dict(
+ base_dn=dict(
+ type="str",
+ required=True,
+ ),
+ attribute=dict(
+ type="str",
+ default="uid",
+ ),
+ name_attribute=dict(
+ type="str",
+ default="cn",
+ ),
+ object_class=dict(
+ type="str",
+ default="person",
+ ),
+ ),
+ ),
+ ),
+ ),
+ groups_prefix=dict(
+ type="str",
+ ),
+ username_prefix=dict(
+ type="str",
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "authproviders", module.params["name"]
+ )
+
+ payload = dict(
+ type="ldap",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=arguments.get_spec_payload(
+ module.params, "servers", "groups_prefix", "username_prefix"
+ ),
+ )
+
+ try:
+ changed, ldap_provider = utils.sync_v1(
+ module.params["state"], client, path, payload, module.check_mode, do_differ
+ )
+ module.exit_json(changed=changed, object=remove_item(ldap_provider))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/mutator.py b/ansible_collections/sensu/sensu_go/plugins/modules/mutator.py
new file mode 100644
index 00000000..0baf8ca8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/mutator.py
@@ -0,0 +1,154 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: mutator
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu mutators
+description:
+ - Create, update or delete Sensu mutator.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/mutators/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+ - sensu.sensu_go.secrets
+seealso:
+ - module: sensu.sensu_go.mutator_info
+options:
+ command:
+ description:
+ - The mutator command to be executed by the Sensu backend.
+ - Required if I(state) is C(present).
+ type: str
+ timeout:
+ description:
+ - The mutator execution duration timeout in seconds (hard stop).
+ type: int
+ env_vars:
+ description:
+ - A mapping of environment variable names and values to use with command execution.
+ type: dict
+ runtime_assets:
+ description:
+ - List of runtime assets, required to run the mutator I(command).
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a mutator
+ sensu.sensu_go.mutator:
+ 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
+
+- name: Delete a mutator
+ sensu.sensu_go.mutator:
+ name: mutator
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu mutator.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ annotations: null
+ labels: null
+ name: example-mutator
+ namespace: default
+ command: example_mutator.go
+ env_vars: []
+ runtime_assets: []
+ timeout: 0
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def do_differ(current, desired):
+ return (
+ utils.do_differ(current, desired, "secrets") or
+ utils.do_secrets_differ(current, desired)
+ )
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['command'])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ "secrets",
+ ),
+ command=dict(),
+ timeout=dict(
+ type='int',
+ ),
+ env_vars=dict(
+ type='dict'
+ ),
+ runtime_assets=dict(
+ type='list', elements='str',
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'mutators', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'command', 'timeout', 'runtime_assets', 'secrets',
+ )
+ if module.params['env_vars']:
+ payload['env_vars'] = utils.dict_to_key_value_strings(module.params['env_vars'])
+ try:
+ changed, mutator = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ do_differ,
+ )
+ module.exit_json(changed=changed, object=mutator)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/mutator_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/mutator_info.py
new file mode 100644
index 00000000..1e247205
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/mutator_info.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: mutator_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu mutators
+description:
+ - Retrieve information about Sensu mutators.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/mutators/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.mutator
+'''
+
+EXAMPLES = '''
+- name: List all Sensu mutators
+ sensu.sensu_go.mutator_info:
+ register: result
+
+- name: Retrieve a single Sensu mutator
+ sensu.sensu_go.mutator_info:
+ name: my-mutator
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu mutators.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ annotations: null
+ labels: null
+ name: example-mutator
+ namespace: default
+ command: example_mutator.go
+ env_vars: []
+ runtime_assets: []
+ timeout: 0
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "mutators", module.params["name"],
+ )
+
+ try:
+ mutators = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=mutators)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/namespace.py b/ansible_collections/sensu/sensu_go/plugins/modules/namespace.py
new file mode 100644
index 00000000..64d174cb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/namespace.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: namespace
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu namespaces
+description:
+ - Create, update or delete a Sensu namespace.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#namespaces).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.namespace_info
+'''
+
+EXAMPLES = '''
+- name: Create a new namespace
+ sensu.sensu_go.namespace:
+ name: production
+ state: present
+
+- name: Delete a namespace
+ sensu.sensu_go.namespace:
+ name: staging
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu namespace.
+ returned: success
+ type: dict
+ sample:
+ name: default
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=arguments.get_spec("auth", "name", "state"),
+ )
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ None, 'namespaces', module.params['name'],
+ )
+ payload = arguments.get_spec_payload(
+ module.params, 'name'
+ )
+ try:
+ changed, namespace = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=namespace)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/namespace_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/namespace_info.py
new file mode 100644
index 00000000..d5d0a269
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/namespace_info.py
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+#
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: namespace_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu namespaces
+description:
+ - Retrieve information about Sensu namespaces.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#namespaces).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+notes:
+ - Currently, it is not possible to retrieve information about a single
+ namespace because namespace is not much more than a name itself.
+seealso:
+ - module: sensu.sensu_go.namespace
+'''
+
+EXAMPLES = '''
+- name: List Sensu namespaces
+ sensu.sensu_go.namespace_info:
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu namespaces.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - name: default
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ ),
+ )
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(None, 'namespaces')
+
+ try:
+ namespaces = utils.get(client, path)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=namespaces)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/oidc_auth_provider.py b/ansible_collections/sensu/sensu_go/plugins/modules/oidc_auth_provider.py
new file mode 100644
index 00000000..bcd5d4ed
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/oidc_auth_provider.py
@@ -0,0 +1,248 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: oidc_auth_provider
+
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+
+short_description: Manage Sensu OIDC authentication provider
+
+description:
+ - Create, update or delete a Sensu Go OIDC authentication provider.
+ - For more information, refer to the Sensu Go documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/control-access/oidc-auth/).
+
+version_added: 1.10.0
+
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+
+options:
+ additional_scopes:
+ description:
+ - Scopes to include in the claims.
+ type: list
+ elements: str
+ default: openid
+ client_id:
+ description:
+ - The OIDC provider application Client ID.
+ - Required if I(state) is C(present).
+ type: str
+ client_secret:
+ description:
+ - The OIDC provider application Client Secret.
+ - Required if I(state) is C(present).
+ type: str
+ disable_offline_access:
+ description:
+ - If C(true), the OIDC provider cannot include the offline_access scope
+ in the authentication request. Otherwise, C(false).
+ type: bool
+ default: false
+ redirect_uri:
+ description:
+ - Redirect URL to provide to the OIDC provider.
+ type: str
+ server:
+ description:
+ - The location of the OIDC server you wish to authenticate against.
+ - Required if I(state) is C(present).
+ type: str
+ groups_claim:
+ description:
+ - The claim to use to form the associated RBAC groups.
+ type: str
+ groups_prefix:
+ description:
+ - The prefix added to all OIDC groups.
+ type: str
+ username_claim:
+ description:
+ - The claim to use to form the final RBAC user name.
+ - Required if I(state) is C(present).
+ type: str
+ username_prefix:
+ description:
+ - The prefix added to all OIDC usernames.
+ type: str
+
+seealso:
+ - module: sensu.sensu_go.auth_provider_info
+ - module: sensu.sensu_go.ldap_auth_provider
+ - module: sensu.sensu_go.ad_auth_provider
+
+notes:
+ - Supported only on Sensu Go versions >= 6.
+"""
+
+EXAMPLES = """
+- name: Create a OIDC auth provider
+ sensu.sensu_go.oidc_auth_provider:
+ state: present
+ name: oidc_name
+ additional_scopes:
+ - groups
+ - email
+ 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:'
+
+- name: Delete a OIDC auth provider
+ sensu.sensu_go.oidc_auth_provider:
+ name: oidc_name
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu OIDC authentication provider.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: 'oidc_name'
+ created_by: 'admin'
+ additional_scopes:
+ - 'groups'
+ - 'email'
+ client_id: 'a8e43af034e7f2608780'
+ disable_offline_access: false
+ redirect_uri: 'http://sensu-backend.example.com: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:'
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "authentication/v2"
+
+
+def remove_item(result):
+ if result and 'client_secret' in result:
+ del result['client_secret']
+
+ return result
+
+
+def main():
+ required_if = [
+ ("state", "present", ["client_id", "client_secret", "server", "username_claim"])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth",
+ "name",
+ "state",
+ ),
+ additional_scopes=dict(
+ type="list",
+ elements="str",
+ default="openid",
+ ),
+ client_id=dict(
+ type="str",
+ ),
+ client_secret=dict(
+ type="str",
+ no_log=True,
+ ),
+ disable_offline_access=dict(
+ type="bool",
+ default=False,
+ ),
+ redirect_uri=dict(
+ type="str",
+ ),
+ server=dict(
+ type="str",
+ ),
+ groups_claim=dict(
+ type="str",
+ ),
+ groups_prefix=dict(
+ type="str",
+ ),
+ username_claim=dict(
+ type="str",
+ ),
+ username_prefix=dict(
+ type="str",
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "authproviders", module.params["name"]
+ )
+
+ payload = dict(
+ type="oidc",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=arguments.get_spec_payload(
+ module.params,
+ "additional_scopes",
+ "client_id",
+ "client_secret",
+ "disable_offline_access",
+ "redirect_uri",
+ "server",
+ "groups_claim",
+ "groups_prefix",
+ "username_claim",
+ "username_prefix",
+ ),
+ )
+
+ try:
+ changed, oidc_provider = utils.sync_v1(
+ module.params["state"], client, path, payload, module.check_mode
+ )
+ module.exit_json(
+ changed=changed, object=remove_item(oidc_provider)
+ )
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/pipe_handler.py b/ansible_collections/sensu/sensu_go/plugins/modules/pipe_handler.py
new file mode 100644
index 00000000..99e985f7
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/pipe_handler.py
@@ -0,0 +1,167 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: pipe_handler
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu pipe handler
+description:
+ - Create, update or delete a Sensu pipe handler.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/handlers/#pipe-handlers).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+ - sensu.sensu_go.secrets
+seealso:
+ - module: sensu.sensu_go.socket_handler
+ - module: sensu.sensu_go.handler_info
+ - module: sensu.sensu_go.handler_set
+options:
+ command:
+ description:
+ - The handler command to be executed. The event data is passed to the
+ process through STDIN.
+ - Required if I(state) is C(present).
+ type: str
+ filters:
+ description:
+ - List of filters to use when determining whether to pass the check result to this handler.
+ type: list
+ elements: str
+ mutator:
+ description:
+ - Mutator to call for transforming the check result before passing it to this handler.
+ type: str
+ timeout:
+ description:
+ - Timeout for handler execution.
+ type: int
+ env_vars:
+ description:
+ - A mapping of environment variable names and values to use with command execution.
+ type: dict
+ runtime_assets:
+ description:
+ - List of runtime assets to required to run the handler C(command).
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Setup InfluxDB handler
+ sensu.sensu_go.pipe_handler:
+ name: influx-db
+ command: sensu-influxdb-handler -d sensu
+ env_vars:
+ INFLUXDB_ADDR: http://influxdb.default.svc.cluster.local:8086
+ INFLUXDB_USER: sensu
+ INFLUXDB_PASS: password
+ runtime_assets:
+ - sensu-influxdb-handler
+
+- name: Delete handler
+ sensu.sensu_go.pipe_handler:
+ name: influx-db
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu pipe handler.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: pipe_handler_minimum
+ namespace: default
+ command: command-example
+ type: pipe
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def do_differ(current, desired):
+ return (
+ utils.do_differ(current, desired, "secrets") or
+ utils.do_secrets_differ(current, desired)
+ )
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['command'])
+ ]
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_if=required_if,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ "secrets",
+ ),
+ command=dict(),
+ filters=dict(
+ type='list', elements='str',
+ ),
+ mutator=dict(),
+ timeout=dict(
+ type='int'
+ ),
+ env_vars=dict(
+ type='dict'
+ ),
+ runtime_assets=dict(
+ type='list', elements='str',
+ ),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'handlers', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'command', 'filters', 'mutator', 'timeout',
+ 'runtime_assets', 'secrets',
+ )
+ payload['type'] = 'pipe'
+ if module.params['env_vars']:
+ payload['env_vars'] = utils.dict_to_key_value_strings(module.params['env_vars'])
+
+ try:
+ changed, handler = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ do_differ,
+ )
+ module.exit_json(changed=changed, object=handler)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/role.py b/ansible_collections/sensu/sensu_go/plugins/modules/role.py
new file mode 100644
index 00000000..54f58d13
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/role.py
@@ -0,0 +1,165 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: role
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu roles
+description:
+ - Create, update or delete Sensu role.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#roles-and-cluster-roles).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.role_info
+ - module: sensu.sensu_go.cluster_role
+ - module: sensu.sensu_go.role_binding
+options:
+ rules:
+ description:
+ - Rules that the role applies.
+ - Must be non-empty if I(state) is C(present).
+ type: list
+ elements: dict
+ suboptions:
+ verbs:
+ description:
+ - Permissions to be applied by the rule.
+ type: list
+ elements: str
+ required: yes
+ choices: [get, list, create, update, delete]
+ resources:
+ description:
+ - Types of resources the rule has permission to access.
+ type: list
+ elements: str
+ required: yes
+ resource_names:
+ description:
+ - Names of specific resources the rule has permission to access.
+ - Note that for the C(create) verb, this argument will not be
+ taken into account when enforcing RBAC, even if it is provided.
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a role
+ sensu.sensu_go.role:
+ name: readonly
+ rules:
+ - verbs:
+ - get
+ - list
+ resources:
+ - checks
+ - entities
+
+- name: Delete a role
+ sensu.sensu_go.role:
+ name: readonly
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu role.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: namespaced-resources-all-verbs
+ namespace: default
+ rules:
+ - resource_names: []
+ resources:
+ - assets
+ - checks
+ verbs:
+ - create
+ - update
+ - delete
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils, role_utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state", "namespace"),
+ rules=dict(
+ type="list",
+ elements="dict",
+ options=dict(
+ verbs=dict(
+ required=True,
+ type="list",
+ elements="str",
+ choices=["get", "list", "create", "update", "delete"],
+ ),
+ resources=dict(
+ required=True,
+ type="list",
+ elements="str",
+ ),
+ resource_names=dict(
+ type="list",
+ elements="str",
+ ),
+ )
+ )
+ )
+ )
+
+ msg = role_utils.validate_module_params(module.params)
+ if msg:
+ module.fail_json(msg=msg)
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "roles", module.params["name"],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, "rules"
+ )
+
+ try:
+ changed, role = utils.sync(
+ module.params['state'], client, path,
+ payload, module.check_mode, role_utils.do_roles_differ
+ )
+ module.exit_json(changed=changed, object=role)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/role_binding.py b/ansible_collections/sensu/sensu_go/plugins/modules/role_binding.py
new file mode 100644
index 00000000..0bab8b1b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/role_binding.py
@@ -0,0 +1,176 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: role_binding
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu role bindings
+description:
+ - Create, update or delete Sensu role binding.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#role-bindings-and-cluster-role-bindings).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.role_binding_info
+ - module: sensu.sensu_go.role
+ - module: sensu.sensu_go.cluster_role
+ - module: sensu.sensu_go.cluster_role_binding
+options:
+ role:
+ description:
+ - Name of the role.
+ - This parameter is mutually exclusive with I(cluster_role).
+ type: str
+ cluster_role:
+ description:
+ - Name of the cluster role. Note that the resulting role
+ binding grants the cluster role to the provided users and
+ groups in the context of I(auth.namespace) only.
+ - This parameter is mutually exclusive with I(role).
+ type: str
+ users:
+ description:
+ - List of users to bind to the role or cluster role.
+ - Note that at least one of I(users) and I(groups) must be
+ specified when creating a role binding.
+ type: list
+ elements: str
+ groups:
+ description:
+ - List of groups to bind to the role or cluster role.
+ - Note that at least one of I(users) and I(groups) must be
+ specified when creating a role binding.
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a role binding
+ sensu.sensu_go.role_binding:
+ name: dev_and_testing
+ role: testers_permissive
+ groups:
+ - testers
+ - dev
+ - ops
+ users:
+ - alice
+
+- name: Create a role binding for admins
+ sensu.sensu_go.role_binding:
+ name: org-admins
+ cluster_role: admin
+ groups:
+ - team1-admins
+ - team2-admins
+
+- name: Delete a role binding
+ sensu.sensu_go.role_binding:
+ name: org-admins
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu role binding.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: event-reader-binding
+ namespace: default
+ role_ref:
+ name: event-reader
+ type: Role
+ subjects:
+ - name: bob
+ type: User
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils, role_utils
+
+
+def infer_role(params):
+ if params["role"]:
+ return "Role", params["role"]
+ return "ClusterRole", params["cluster_role"]
+
+
+def build_api_payload(params):
+ payload = arguments.get_mutation_payload(params)
+ payload["subjects"] = role_utils.build_subjects(params["groups"], params["users"])
+ payload["role_ref"] = role_utils.type_name_dict(*infer_role(params))
+
+ return payload
+
+
+def main():
+ required_if = [
+ ("state", "present", ["role", "cluster_role"], True) # True means any of role, cluster_role
+ ]
+ mutually_exclusive = [
+ ["role", "cluster_role"]
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ mutually_exclusive=mutually_exclusive,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state", "namespace"),
+ role=dict(),
+ cluster_role=dict(),
+ users=dict(
+ type="list", elements="str",
+ ),
+ groups=dict(
+ type="list", elements="str",
+ ),
+ )
+ )
+
+ msg = role_utils.validate_binding_module_params(module.params)
+ if msg:
+ module.fail_json(msg=msg)
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "rolebindings", module.params["name"],
+ )
+ payload = build_api_payload(module.params)
+
+ try:
+ changed, role_binding = utils.sync(
+ module.params["state"], client, path, payload, module.check_mode, role_utils.do_role_bindings_differ
+ )
+ module.exit_json(changed=changed, object=role_binding)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/role_binding_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/role_binding_info.py
new file mode 100644
index 00000000..ef7d230f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/role_binding_info.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: role_binding_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu role bindings
+description:
+ - Retrieve information about Sensu role bindings.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#role-bindings-and-cluster-role-bindings).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.role_binding
+'''
+
+EXAMPLES = '''
+- name: List all Sensu role bindings
+ sensu.sensu_go.role_binding_info:
+ register: result
+
+- name: Retrieve a single Sensu role binding
+ sensu.sensu_go.role_binding_info:
+ name: my-role-binding
+ register: result
+'''
+
+RETURN = '''
+role_bindings:
+ description: List of Sensu role bindings.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: event-reader-binding
+ namespace: default
+ role_ref:
+ name: event-reader
+ type: Role
+ subjects:
+ - name: bob
+ type: User
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict()
+ )
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "rolebindings", module.params["name"],
+ )
+
+ try:
+ role_bindings = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=role_bindings)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/role_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/role_info.py
new file mode 100644
index 00000000..cf2c7e8f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/role_info.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: role_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu roles
+description:
+ - Retrieve information about Sensu roles.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#roles-and-cluster-roles).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.role
+'''
+
+EXAMPLES = '''
+- name: List all Sensu roles
+ sensu.sensu_go.role_info:
+ register: result
+
+- name: Retrieve a specific Sensu role
+ sensu.sensu_go.role_info:
+ name: my-role
+ register: result
+'''
+
+RETURN = '''
+roles:
+ description: List of Sensu roles.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: namespaced-resources-all-verbs
+ namespace: default
+ rules:
+ - resource_names: []
+ resources:
+ - assets
+ - checks
+ verbs:
+ - create
+ - update
+ - delete
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict()
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "roles", module.params["name"],
+ )
+
+ try:
+ roles = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=roles)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/secret.py b/ansible_collections/sensu/sensu_go/plugins/modules/secret.py
new file mode 100644
index 00000000..6b012557
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/secret.py
@@ -0,0 +1,131 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: secret
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu Go secrets
+description:
+ - Create, update or delete Sensu secret.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/manage-secrets/secrets/).
+version_added: 1.6.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.secret_info
+ - module: sensu.sensu_go.secrets_provider_env
+ - module: sensu.sensu_go.secrets_provider_vault
+ - module: sensu.sensu_go.secrets_provider_info
+options:
+ provider:
+ description:
+ - Name of the secrets provider that backs the secret value.
+ - Required if I(state) is C(present).
+ type: str
+ id:
+ description:
+ - Secret's id in the provider store.
+ - Required if I(state) is C(present).
+ type: str
+"""
+
+EXAMPLES = """
+- name: Create an environment varibale-backed secret
+ sensu.sensu_go.secret:
+ name: env_secret
+ provider: env
+ id: MY_ENV_VARIABLE
+
+- name: Create a HashiCorp Vault-backed secret
+ sensu.sensu_go.secret:
+ name: hashi_valut_secret
+ provider: vault
+ id: secret/store#name
+
+- name: Delete a secret
+ sensu.sensu_go.secret:
+ name: my_secret
+ state: absent
+"""
+
+RETURN = """
+object:
+ description: Object representing Sensu secret.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: sensu-ansible
+ namespace: default
+ id: 'secret/database#password'
+ provider: vault
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "secrets/v1"
+
+
+def main():
+ required_if = [
+ ("state", "present", ["provider", "id"])
+ ]
+ module = AnsibleModule(
+ required_if=required_if,
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name", "state", "namespace"),
+ provider=dict(type="str"),
+ id=dict(type="str"),
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, module.params["namespace"], "secrets",
+ module.params["name"],
+ )
+ payload = dict(
+ type="Secret",
+ api_version=API_VERSION,
+ metadata=dict(
+ name=module.params["name"],
+ namespace=module.params["namespace"],
+ ),
+ spec=arguments.get_spec_payload(module.params, "provider", "id"),
+ )
+ try:
+ changed, secret = utils.sync_v1(
+ module.params["state"], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=secret)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/secret_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/secret_info.py
new file mode 100644
index 00000000..a16ca82a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/secret_info.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: secret_info
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+short_description: List available Sensu Go secrets
+description:
+ - Retrieve information about Sensu Go secrets.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/manage-secrets/secrets/).
+version_added: 1.6.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.secret
+ - module: sensu.sensu_go.secrets_provider_env
+ - module: sensu.sensu_go.secrets_provider_vault
+ - module: sensu.sensu_go.secrets_provider_info
+"""
+
+EXAMPLES = """
+- name: List all Sensu Go secrets
+ sensu.sensu_go.secret_info:
+ register: result
+
+- name: Retrieve the selected Sensu Go secret
+ sensu.sensu_go.secret_info:
+ name: my-secret
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.id }}"
+"""
+
+RETURN = """
+objects:
+ description: List of Sensu Go secrets.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: sensu-ansible-token
+ namespace: default
+ id: ANSIBLE_TOKEN
+ provider: env
+ - metadata:
+ name: sensu-ansible
+ namespace: default
+ id: 'secret/database#password'
+ provider: vault
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "secrets/v1"
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "namespace"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, module.params["namespace"], "secrets",
+ module.params["name"],
+ )
+
+ try:
+ secrets = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ # We simulate the behavior of v2 API here and only return the spec.
+ module.exit_json(changed=False, objects=[
+ utils.convert_v1_to_v2_response(s) for s in secrets
+ ])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_env.py b/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_env.py
new file mode 100644
index 00000000..258b6db0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_env.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: secrets_provider_env
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu Env secrets provider
+description:
+ - Create or delete a Sensu Go Env secrets provider.
+ - The module operates on a secrets provider named C(env).
+ - For more information, refer to the Sensu Go documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/manage-secrets/secrets-providers/).
+version_added: 1.6.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.state
+seealso:
+ - module: sensu.sensu_go.secrets_provider_vault
+ - module: sensu.sensu_go.secrets_provider_info
+ - module: sensu.sensu_go.secret
+ - module: sensu.sensu_go.secret_info
+'''
+
+EXAMPLES = '''
+- name: Create the env secrets provider
+ sensu.sensu_go.secrets_provider_env:
+
+- name: Delete the env secrets provider
+ sensu.sensu_go.secrets_provider_env:
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu Env secrets provider.
+ returned: success
+ type: dict
+ sample:
+ - metadata:
+ name: env
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "secrets/v1"
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "state",
+ ),
+ )
+ )
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, 'providers', 'env'
+ )
+ payload = dict(
+ type="Env",
+ api_version=API_VERSION,
+ metadata=dict(name='env'),
+ spec={},
+ )
+
+ try:
+ changed, env_provider = utils.sync_v1(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=env_provider)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_info.py
new file mode 100644
index 00000000..b3aa1b0e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_info.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = """
+module: secrets_provider_info
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu secrets providers
+description:
+ - Retrieve information about Sensu Go secrets providers.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/manage-secrets/secrets-providers/).
+version_added: 1.6.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.secrets_provider_env
+ - module: sensu.sensu_go.secrets_provider_vault
+ - module: sensu.sensu_go.secret
+ - module: sensu.sensu_go.secret_info
+"""
+
+EXAMPLES = """
+- name: List all Sensu secrets providers
+ sensu.sensu_go.secrets_provider_info:
+ register: result
+
+- name: List the selected Sensu secrets provider
+ sensu.sensu_go.secrets_provider_info:
+ name: my_provider
+ register: result
+
+- name: Do something with result
+ ansible.builtin.debug:
+ msg: "{{ result.objects.0.metadata.name }}"
+"""
+
+RETURN = """
+objects:
+ description: List of Sensu secrets providers.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ name: vault
+ client:
+ address: https://vaultserver.example.com:8200
+ token: VAULT_TOKEN
+ version: v1
+ tls:
+ ca_cert: "/etc/ssl/certs/vault_ca_cert.pem"
+ max_retries: 2
+ timeout: 20s
+ rate_limiter:
+ limit: 10
+ burst: 100
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "secrets/v1"
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, "providers", module.params["name"],
+ )
+
+ try:
+ providers = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ # We simulate the behavior of v2 API here and only return the spec.
+ module.exit_json(changed=False, objects=[
+ utils.convert_v1_to_v2_response(p) for p in providers
+ ])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_vault.py b/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_vault.py
new file mode 100644
index 00000000..2df0a310
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/secrets_provider_vault.py
@@ -0,0 +1,258 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: secrets_provider_vault
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Miha Dolinar (@mdolin)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu VaultProvider secrets providers
+description:
+ - Create, update or delete a Sensu Go VaultProvider secrets provider.
+ - For more information, refer to the Sensu Go documentation at
+ U(https://docs.sensu.io/sensu-go/latest/operations/manage-secrets/secrets-providers/).
+version_added: 1.6.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.state
+options:
+ address:
+ description:
+ - Address of the Vault server.
+ - Required if I(state) is C(present).
+ type: str
+ token:
+ description:
+ - Authentication token to use with Vault.
+ - Required if I(state) is C(present).
+ type: str
+ version:
+ description:
+ - Version of the Vault key/value store.
+ - Please refer to U(https://www.vaultproject.io/docs/secrets/kv) for
+ additional information.
+ - Required if I(state) is C(present).
+ type: str
+ choices: [v1, v2]
+ timeout:
+ description:
+ - Timeout (in seconds) for connection to Vault server.
+ type: int
+ max_retries:
+ description:
+ - Maximum number of times to retry failed connections to Vault server.
+ type: int
+ rate_limit:
+ description:
+ - Maximum number of secrets requests for per second.
+ type: float
+ burst_limit:
+ description:
+ - Maximum allowed number of secrets requests in a rate interval.
+ type: int
+ tls:
+ description:
+ - TLS configuration for establishing connection with Vault server.
+ type: dict
+ suboptions:
+ ca_cert:
+ description:
+ - Path to the certificate file of the trusted certificate authority.
+ type: str
+ client_cert:
+ description:
+ - Path to the client certificate file.
+ type: str
+ client_key:
+ description:
+ - Path to the client key file.
+ type: str
+ cname:
+ description:
+ - Canonical name for the client.
+ type: str
+
+seealso:
+ - module: sensu.sensu_go.secrets_provider_env
+ - module: sensu.sensu_go.secrets_provider_info
+ - module: sensu.sensu_go.secret
+ - module: sensu.sensu_go.secret_info
+'''
+
+EXAMPLES = '''
+- name: Create a vault secrets provider
+ sensu.sensu_go.secrets_provider_vault:
+ name: my-vault
+ address: https://my-vault.com
+ token: VAULT_TOKEN
+ version: v1
+
+- name: Delete a vault secrets provider
+ sensu.sensu_go.secrets_provider_vault:
+ name: my-vault
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu vault secrets provider.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ name: vault
+ client:
+ address: https://vaultserver.example.com:8200
+ token: VAULT_TOKEN
+ version: v1
+ tls:
+ ca_cert: "/etc/ssl/certs/vault_ca_cert.pem"
+ max_retries: 2
+ timeout: 20s
+ rate_limiter:
+ limit: 10
+ burst: 100
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+API_GROUP = "enterprise"
+API_VERSION = "secrets/v1"
+
+
+def do_differ(current, desired):
+ if utils.do_differ_v1(current, desired, "client"):
+ return True
+
+ current_client = current["spec"]["client"]
+ desired_client = desired["spec"]["client"]
+ # Sensu Go API returns 'agent_address' field in the client spec,
+ # but this field is not meant to be set via the providers API.
+ if utils.do_differ(current_client, desired_client, "agent_address", "tls"):
+ return True
+ # Sensu Go API returns some extra fields in the tls spec.
+ # We ignore them, as they are not meant to be set via the
+ # providers API.
+ return utils.do_differ(current_client["tls"], desired_client.get("tls") or {},
+ "insecure", "tls_server_name", "ca_path")
+
+
+def _format_seconds(seconds):
+ # Sensu API returns the configured timeout as a string, for instance
+ # 30 -> '30s', 60-> '1m0s', 3600 -> '1h0m0s'.
+ h, r = divmod(seconds, 3600)
+ m, s = divmod(r, 60)
+ if h:
+ return "{0}h{1}m{2}s".format(h, m, s)
+ if m:
+ return "{0}m{1}s".format(m, s)
+ return "{0}s".format(seconds)
+
+
+def build_vault_provider_spec(params):
+ if params["state"] == "absent":
+ return {}
+
+ client = arguments.get_spec_payload(
+ params, "address", "token", "version", "max_retries",
+ )
+ if params.get("tls"):
+ client["tls"] = arguments.get_spec_payload(
+ params["tls"], "ca_cert", "client_cert", "client_key", "cname",
+ )
+ if params.get("timeout"):
+ client["timeout"] = _format_seconds(params["timeout"])
+
+ if params.get("rate_limit") or params.get("burst_limit"):
+ client["rate_limiter"] = arguments.get_renamed_spec_payload(
+ params, dict(
+ rate_limit="limit",
+ burst_limit="burst",
+ )
+ )
+
+ return dict(client=client)
+
+
+def main():
+ required_if = [
+ ("state", "present", ["address", "token", "version"])
+ ]
+
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_if=required_if,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state",
+ ),
+ address=dict(),
+ token=dict(no_log=True),
+ version=dict(
+ choices=["v1", "v2"],
+ ),
+ timeout=dict(
+ type="int",
+ ),
+ max_retries=dict(
+ type="int",
+ ),
+ rate_limit=dict(
+ type="float",
+ ),
+ burst_limit=dict(
+ type="int",
+ ),
+ tls=dict(
+ type="dict",
+ options=dict(
+ ca_cert=dict(),
+ cname=dict(),
+ client_cert=dict(),
+ client_key=dict(no_log=False),
+ )
+ )
+ )
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_url_path(
+ API_GROUP, API_VERSION, None, 'providers', module.params['name']
+ )
+
+ payload = dict(
+ type="VaultProvider",
+ api_version=API_VERSION,
+ metadata=dict(name=module.params["name"]),
+ spec=build_vault_provider_spec(module.params)
+ )
+
+ try:
+ changed, vault_provider = utils.sync_v1(
+ module.params['state'], client, path, payload, module.check_mode, do_differ
+ )
+ module.exit_json(changed=changed, object=vault_provider)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/silence.py b/ansible_collections/sensu/sensu_go/plugins/modules/silence.py
new file mode 100644
index 00000000..4b287911
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/silence.py
@@ -0,0 +1,172 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: silence
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu silences
+description:
+ - Create, update or delete Sensu silence.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/silencing/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.silence_info
+options:
+ subscription:
+ description:
+ - The name of the subscription the entry should match.
+ - If left empty a silencing entry will contain an asterisk in the
+ subscription position. This indicates that any event with a matching
+ check name will be marked as silenced, regardless of the originating
+ entities subscriptions.
+ - Specific entity can also be targeted by taking advantage of per-entity
+ subscription (entity:<entity_name>).
+ - This parameter is required if the I(check) parameter is absent.
+ type: str
+ check:
+ description:
+ - The name of the check the entry should match.
+ - If left empty a silencing entry will contain an asterisk in the check
+ position. This indicates that any event where the originating entities
+ subscriptions match the subscription specified in the entry will be
+ marked as silenced, regardless of the check name.
+ - This parameter is required if the I(subscription) parameter is absent.
+ type: str
+ begin:
+ description:
+ - UNIX time at which silence entry goes into effect.
+ type: int
+ expire:
+ description:
+ - Number of seconds until the silence expires.
+ type: int
+ expire_on_resolve:
+ description:
+ - If the entry should be deleted when a check begins return OK status (resolves).
+ type: bool
+ reason:
+ description:
+ - Reason for silencing.
+ type: str
+'''
+
+EXAMPLES = '''
+- name: Silence a specific check
+ sensu.sensu_go.silence:
+ subscription: proxy
+ check: check-disk
+
+- name: Silence specific check regardless of the originating entities subscription
+ sensu.sensu_go.silence:
+ check: check-cpu
+
+- name: Silence all checks on a specific entity
+ sensu.sensu_go.silence:
+ subscription: entity:important-entity
+ expire: 120
+ reason: rebooting the world
+
+- name: Delete a silencing entry
+ sensu.sensu_go.silence:
+ subscription: entity:important-entity
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu silence.
+ returned: success
+ type: dict
+ sample:
+ metadata:
+ annotations: null
+ labels: null
+ name: entity:i-424242:*
+ namespace: default
+ begin: 1542671205
+ check: null
+ creator: admin
+ expire: -1
+ expire_on_resolve: false
+ reason: null
+ subscription: entity:i-424242
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ required_one_of = [
+ ['subscription', 'check']
+ ]
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_one_of=required_one_of,
+ argument_spec=dict(
+ arguments.get_spec(
+ 'auth', 'state', 'labels', 'annotations', 'namespace',
+ ),
+ subscription=dict(),
+ check=dict(),
+ begin=dict(
+ type='int',
+ ),
+ expire=dict(
+ type='int',
+ ),
+ expire_on_resolve=dict(
+ type='bool'
+ ),
+ reason=dict()
+ ),
+ )
+ name = '{0}:{1}'.format(module.params['subscription'] or '*', module.params['check'] or '*')
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'silenced', name,
+ )
+ # We add name parameter because it is actually required and must match the name that is
+ # autogenerated on the API
+ module.params['name'] = name
+ payload = arguments.get_mutation_payload(
+ module.params, 'subscription', 'check', 'begin', 'expire', 'expire_on_resolve', 'reason'
+ )
+ try:
+ changed, silence = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=silence)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/silence_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/silence_info.py
new file mode 100644
index 00000000..68769902
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/silence_info.py
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: silence_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Manca Bizjak (@mancabizjak)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu silence entries
+description:
+ - Retrieve information about Sensu silences.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/silencing/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.namespace
+seealso:
+ - module: sensu.sensu_go.silence
+options:
+ subscription:
+ description:
+ - The name of the subscription the entry should match. If left empty a silencing entry will
+ contain an asterisk in the subscription position.
+ type: str
+ check:
+ description:
+ - The name of the check the entry should match. If left empty a silencing entry will contain an
+ asterisk in the check position.
+ type: str
+'''
+
+EXAMPLES = '''
+- name: List all Sensu silence entries
+ sensu.sensu_go.silence_info:
+ register: result
+
+- name: Fetch a specific silence with name proxy:awesome_check
+ sensu.sensu_go.silence_info:
+ subscription: proxy
+ check: awesome_check
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu silence entries.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - metadata:
+ annotations: null
+ labels: null
+ name: entity:i-424242:*
+ namespace: default
+ begin: 1542671205
+ check: null
+ creator: admin
+ expire: -1
+ expire_on_resolve: false
+ reason: null
+ subscription: entity:i-424242
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec('auth', 'namespace'),
+ subscription=dict(),
+ check=dict(),
+ ),
+ )
+
+ name = '{0}:{1}'.format(module.params['subscription'] or '*', module.params['check'] or '*')
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(
+ module.params["namespace"], "silenced", None if name == "*:*" else name,
+ )
+
+ try:
+ silences = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=silences)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/socket_handler.py b/ansible_collections/sensu/sensu_go/plugins/modules/socket_handler.py
new file mode 100644
index 00000000..ac42d27c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/socket_handler.py
@@ -0,0 +1,160 @@
+#!/usr/bin/python
+# -*- 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: socket_handler
+author:
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu TCP/UDP handler
+description:
+ - Create, update or delete Sensu socket handler.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/handlers/#tcp-udp-handlers).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+ - sensu.sensu_go.namespace
+ - sensu.sensu_go.state
+ - sensu.sensu_go.labels
+ - sensu.sensu_go.annotations
+seealso:
+ - module: sensu.sensu_go.handler_info
+ - module: sensu.sensu_go.pipe_handler
+ - module: sensu.sensu_go.handler_set
+options:
+ type:
+ description:
+ - The handler type.
+ - Required if I(state) is C(present).
+ choices:
+ - tcp
+ - udp
+ type: str
+ filters:
+ description:
+ - List of filters to use when determining whether to pass the check result to this handler.
+ type: list
+ elements: str
+ mutator:
+ description:
+ - Mutator to call for transforming the check result before passing it to this handler.
+ type: str
+ timeout:
+ description:
+ - Timeout for handler execution.
+ type: int
+ host:
+ description:
+ - The socket host address (IP or hostname) to connect to.
+ - Required if I(state) is C(present).
+ type: str
+ port:
+ description:
+ - The socket port to connect to.
+ - Required if I(state) is C(present).
+ type: int
+'''
+
+EXAMPLES = '''
+- name: TCP handler
+ sensu.sensu_go.socket_handler:
+ name: tcp_handler
+ type: tcp
+ host: 10.0.1.99
+ port: 4444
+
+- name: UDP handler
+ sensu.sensu_go.socket_handler:
+ name: udp_handler
+ type: udp
+ host: 10.0.1.99
+ port: 4444
+
+- name: Delete a handler
+ sensu.sensu_go.socket_handler:
+ name: udp_handler
+ state: absent
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu socket handler.
+ returned: success
+ type: dict
+ sample:
+ - metadata:
+ name: udp_handler
+ namespace: default
+ socket:
+ host: 10.0.1.99
+ port: 4444
+ type: udp
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ required_if = [
+ ('state', 'present', ['type', 'host', 'port'])
+ ]
+ module = AnsibleModule(
+ supports_check_mode=True,
+ required_if=required_if,
+ argument_spec=dict(
+ arguments.get_spec(
+ "auth", "name", "state", "labels", "annotations", "namespace",
+ ),
+ type=dict(choices=['tcp', 'udp']),
+ filters=dict(
+ type='list', elements='str',
+ ),
+ mutator=dict(),
+ timeout=dict(
+ type='int'
+ ),
+ host=dict(),
+ port=dict(
+ type='int'
+ )
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(
+ module.params['namespace'], 'handlers', module.params['name'],
+ )
+ payload = arguments.get_mutation_payload(
+ module.params, 'type', 'filters', 'mutator', 'timeout'
+ )
+ payload['socket'] = dict(host=module.params['host'], port=module.params['port'])
+
+ try:
+ changed, handler = utils.sync(
+ module.params['state'], client, path, payload, module.check_mode,
+ )
+ module.exit_json(changed=changed, object=handler)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/tessen.py b/ansible_collections/sensu/sensu_go/plugins/modules/tessen.py
new file mode 100644
index 00000000..ba18f518
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/tessen.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: tessen
+author:
+ - Paul Arthur (@flowerysong)
+ - Manca Bizjak (@mancabizjak)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu's Tessen configuration
+description:
+ - Enable or disable Tessen service.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/tessen/).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+options:
+ state:
+ description:
+ - Enable or disable sending anonymized data to Sensu Inc.
+ choices: [ enabled, disabled ]
+ type: str
+ required: True
+'''
+
+EXAMPLES = '''
+- name: Disable Tessen
+ sensu.sensu_go.tessen:
+ state: disabled
+ register: result
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu tessen.
+ returned: success
+ type: dict
+ sample:
+ opt_out: false
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def get(client, path):
+ resp = client.get(path)
+ if resp.status != 200:
+ raise errors.SyncError(
+ "GET {0} failed with status {1}: {2}".format(path, resp.status, resp.data))
+ return resp.json
+
+
+def sync(client, path, payload, check_mode):
+ remote_object = get(client, path)
+
+ if utils.do_differ(remote_object, payload):
+ if check_mode:
+ return True, payload
+ utils.put(client, path, payload)
+ return True, get(client, path)
+
+ return False, remote_object
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec('auth'),
+ state=dict(
+ choices=['enabled', 'disabled'],
+ required=True,
+ )
+ )
+ )
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(None, 'tessen')
+ payload = dict(
+ opt_out=module.params['state'] == 'disabled'
+ )
+
+ try:
+ changed, tessen = sync(client, path, payload, module.check_mode)
+ module.exit_json(changed=changed, object=tessen)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/user.py b/ansible_collections/sensu/sensu_go/plugins/modules/user.py
new file mode 100644
index 00000000..4ef15586
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/user.py
@@ -0,0 +1,326 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: user
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Tadej Borovsak (@tadeboro)
+short_description: Manage Sensu users
+description:
+ - Create, update, activate or deactivate Sensu user.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#users).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.name
+requirements:
+ - bcrypt (when managing Sensu Go 5.21.0 or newer)
+seealso:
+ - module: sensu.sensu_go.user_info
+options:
+ state:
+ description:
+ - Desired state of the user.
+ - Users cannot actually be deleted, only deactivated.
+ type: str
+ choices: [ enabled, disabled ]
+ default: enabled
+ password:
+ description:
+ - Password for the user.
+ - Required if user with a desired name does not exist yet on the backend
+ and I(password_hash) is not set.
+ - If both I(password) and I(password_hash) are set, I(password_hash) is
+ ignored and calculated from the I(password) if required.
+ type: str
+ password_hash:
+ description:
+ - Bcrypt password hash for the user.
+ - Use C(sensuctl user hash-password PASSWORD) to generate a hash.
+ - Required if user with a desired name does not exist yet on the backend
+ and I(password) is not set.
+ - If both I(password) and I(password_hash) are set, I(password_hash) is
+ ignored and calculated from the I(password) if required.
+ - Sensu Go < 5.21.0 does not support creating/updating users using
+ hashed passwords. Use I(password) parameter if you need to manage such
+ Sensu Go installations.
+ - At the moment, change detection does not work properly when using
+ password hashes because the Sensu Go backend does not expose enough
+ information via its API.
+ type: str
+ version_added: 1.8.0
+ groups:
+ description:
+ - List of groups user belongs to.
+ type: list
+ elements: str
+'''
+
+EXAMPLES = '''
+- name: Create a user
+ sensu.sensu_go.user:
+ auth:
+ url: http://localhost:8080
+ name: awesome_username
+ password: hidden_password?
+ groups:
+ - dev
+ - prod
+
+- name: Use pre-hashed password
+ sensu.sensu_go.user:
+ auth:
+ url: http://localhost:8080
+ name: awesome_username
+ password_hash: $5f$14$.brXRviMZpbaleSq9kjoUuwm67V/s4IziOLGHjEqxJbzPsreQAyNm
+
+- name: Deactivate a user
+ sensu.sensu_go.user:
+ name: awesome_username
+ state: disabled
+'''
+
+RETURN = '''
+object:
+ description: Object representing Sensu user.
+ returned: success
+ type: dict
+ sample:
+ disabled: false
+ groups:
+ - ops
+ - dev
+ password: USER_PASSWORD
+ password_hash: $5f$14$.brXRviMZpbaleSq9kjoUuwm67V/s4IziOLGHjEqxJbzPsreQAyNm
+ username: alice
+'''
+
+import traceback
+
+from ansible.module_utils.basic import AnsibleModule, missing_required_lib
+
+from ..module_utils import arguments, errors, utils
+
+try:
+ import bcrypt
+ HAS_BCRYPT = True
+ BCRYPT_IMPORT_ERROR = None
+except ImportError:
+ HAS_BCRYPT = False
+ BCRYPT_IMPORT_ERROR = traceback.format_exc()
+
+
+def _simulate_backend_response(payload):
+ # Backend does not return back any password-related information for now.
+ masked_keys = ('password', 'password_hash')
+ return dict(
+ (k, v) for k, v in payload.items() if k not in masked_keys
+ )
+
+
+def update_password(client, path, username, password, check_mode):
+ # Hit the auth testing API and try to validate the credentials. If the API
+ # says they are invalid, we need to update them.
+ if client.validate_auth_data(username, password):
+ return False
+
+ if not check_mode:
+ if client.version < "5.21.0":
+ utils.put(client, path + '/password', dict(
+ username=username, password=password,
+ ))
+ else:
+ hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
+ utils.put(client, path + '/reset_password', dict(
+ username=username, password_hash=hash.decode('ascii'),
+ ))
+
+ return True
+
+
+def update_password_hash(client, path, username, password_hash, check_mode):
+ # Some older Sensu Go versions do not have support for password hashes.
+ if client.version < "5.21.0":
+ raise errors.SensuError(
+ "Sensu Go < 5.21.0 does not support password hashes"
+ )
+
+ # Insert change detection here once we can receive password hash from the
+ # backend. Up until then, we always update passwords.
+
+ if not check_mode:
+ utils.put(client, path + '/reset_password', dict(
+ username=username, password_hash=password_hash,
+ ))
+
+ return True
+
+
+def update_groups(client, path, old_groups, new_groups, check_mode):
+ to_delete = set(old_groups).difference(new_groups)
+ to_add = set(new_groups).difference(old_groups)
+
+ if not check_mode:
+ # Next few lines are far from atomic, which means that we can leave a
+ # user in any of the intermediate states, but this is the best we can
+ # do given the API limitations.
+ for g in to_add:
+ utils.put(client, path + '/groups/' + g, None)
+ for g in to_delete:
+ utils.delete(client, path + '/groups/' + g)
+
+ return len(to_delete) + len(to_add) > 0
+
+
+def update_state(client, path, old_disabled, new_disabled, check_mode):
+ changed = old_disabled != new_disabled
+
+ if not check_mode and changed:
+ if new_disabled: # `state: disabled` input parameter
+ utils.delete(client, path)
+ else: # `state: enabled` input parameter
+ utils.put(client, path + '/reinstate', None)
+
+ return changed
+
+
+def sync(remote_object, client, path, payload, check_mode):
+ # Create new user (either enabled or disabled)
+ if remote_object is None:
+ if check_mode:
+ return True, _simulate_backend_response(payload)
+ utils.put(client, path, payload)
+ return True, utils.get(client, path)
+
+ # Update existing user. We do this on a field-by-field basis because the
+ # upsteam API for updating users requires a password field to be set. Of
+ # course, we do not want to force users to specify an existing password
+ # just for the sake of updating the group membership, so this is why we
+ # use field-specific API endpoints to update the user data.
+
+ changed = False
+
+ # We only use password hash if we do not have a password. In practice,
+ # this means that users should not set both password and password_hash. We
+ # do not enforce this by making those two parameters mutually exclusive
+ # because in the future (2.0.0 version of collection), we intend to move
+ # password hashing into action plugin and supply both the password and its
+ # hash. Why? Because installing bcrypt on control node is way friendlier
+ # compared to installing bcrypt on every host that runs our user module.
+ #
+ # It is true that most of the time, control node == target node in our
+ # cases, but not always.
+ if 'password' in payload:
+ changed = update_password(
+ client, path, payload['username'], payload['password'],
+ check_mode,
+ ) or changed
+ elif 'password_hash' in payload:
+ changed = update_password_hash(
+ client, path, payload['username'], payload['password_hash'],
+ check_mode,
+ ) or changed
+
+ if 'groups' in payload:
+ changed = update_groups(
+ client, path, remote_object.get('groups') or [],
+ payload['groups'], check_mode,
+ ) or changed
+
+ if 'disabled' in payload:
+ changed = update_state(
+ client, path, remote_object['disabled'], payload['disabled'],
+ check_mode,
+ ) or changed
+
+ if check_mode:
+ # Backend does not return back passwords, so we should follow the
+ # example set by the backend API.
+ return changed, dict(
+ remote_object, **_simulate_backend_response(payload)
+ )
+
+ return changed, utils.get(client, path)
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth", "name"),
+ state=dict(
+ default='enabled',
+ choices=['enabled', 'disabled'],
+ ),
+ password=dict(
+ no_log=True
+ ),
+ password_hash=dict(
+ no_log=False, # Showing hashes is perfectly OK
+ ),
+ groups=dict(
+ type='list', elements='str',
+ )
+ ),
+ )
+
+ client = arguments.get_sensu_client(module.params['auth'])
+ path = utils.build_core_v2_path(None, 'users', module.params['name'])
+
+ try:
+ if not HAS_BCRYPT and client.version >= "5.21.0":
+ module.fail_json(
+ msg=missing_required_lib('bcrypt'),
+ exception=BCRYPT_IMPORT_ERROR,
+ )
+ except errors.SensuError as e:
+ module.fail_json(msg=str(e))
+
+ try:
+ remote_object = utils.get(client, path)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ if (
+ remote_object is None
+ and module.params['password'] is None
+ and module.params['password_hash'] is None
+ ):
+ module.fail_json(
+ msg='Cannot create new user without a password or a hash'
+ )
+
+ payload = arguments.get_spec_payload(
+ module.params, 'password', 'password_hash', 'groups',
+ )
+ payload['username'] = module.params['name']
+ payload['disabled'] = module.params['state'] == 'disabled'
+
+ try:
+ changed, user = sync(
+ remote_object, client, path, payload, module.check_mode
+ )
+ module.exit_json(changed=changed, object=user)
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/plugins/modules/user_info.py b/ansible_collections/sensu/sensu_go/plugins/modules/user_info.py
new file mode 100644
index 00000000..ad9eda8f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/plugins/modules/user_info.py
@@ -0,0 +1,90 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, Paul Arthur <paul.arthur@flowerysong.com>
+# 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
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["stableinterface"],
+ "supported_by": "certified",
+}
+
+DOCUMENTATION = '''
+module: user_info
+author:
+ - Paul Arthur (@flowerysong)
+ - Aljaz Kosir (@aljazkosir)
+ - Miha Plesko (@miha-plesko)
+ - Tadej Borovsak (@tadeboro)
+short_description: List Sensu users
+description:
+ - Retrieve information about Sensu users.
+ - For more information, refer to the Sensu documentation at
+ U(https://docs.sensu.io/sensu-go/latest/reference/rbac/#users).
+version_added: 1.0.0
+extends_documentation_fragment:
+ - sensu.sensu_go.requirements
+ - sensu.sensu_go.auth
+ - sensu.sensu_go.info
+seealso:
+ - module: sensu.sensu_go.user
+'''
+
+EXAMPLES = '''
+- name: List Sensu users
+ sensu.sensu_go.user_info:
+ register: result
+
+- name: Retrieve a single Sensu user
+ sensu.sensu_go.user_info:
+ name: my-user
+ register: result
+'''
+
+RETURN = '''
+objects:
+ description: List of Sensu users.
+ returned: success
+ type: list
+ elements: dict
+ sample:
+ - disabled: false
+ groups:
+ - ops
+ - dev
+ password: USER_PASSWORD
+ password_hash: $5f$14$.brXRviMZpbaleSq9kjoUuwm67V/s4IziOLGHjEqxJbzPsreQAyNm
+ username: alice
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils import arguments, errors, utils
+
+
+def main():
+ module = AnsibleModule(
+ supports_check_mode=True,
+ argument_spec=dict(
+ arguments.get_spec("auth"),
+ name=dict(), # Name is not required in info modules.
+ ),
+ )
+ client = arguments.get_sensu_client(module.params["auth"])
+ path = utils.build_core_v2_path(None, "users", module.params["name"])
+
+ try:
+ users = utils.prepare_result_list(utils.get(client, path))
+ except errors.Error as e:
+ module.fail_json(msg=str(e))
+
+ module.exit_json(changed=False, objects=users)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/sensu/sensu_go/pytest.ini b/ansible_collections/sensu/sensu_go/pytest.ini
new file mode 100644
index 00000000..36542343
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+junit_family = xunit1
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/README.md b/ansible_collections/sensu/sensu_go/roles/agent/README.md
new file mode 100644
index 00000000..24d817ab
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/README.md
@@ -0,0 +1,5 @@
+# sensu.sensu_go.agent role
+
+Visit [the official documentation site][docs] for role documentation.
+
+ [docs]: https://sensu.github.io/sensu-go-ansible/roles/agent.html
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/defaults/main.yml b/ansible_collections/sensu/sensu_go/roles/agent/defaults/main.yml
new file mode 100644
index 00000000..a6238675
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# Related to /etc/sensu/backend.yml, see
+# https://docs.sensu.io/sensu-go/latest/reference/agent/#configuration-summary
+agent_backend_urls: "{{ hostvars | sensu.sensu_go.backends(groups) }}"
+agent_config:
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/handlers/main.yml b/ansible_collections/sensu/sensu_go/roles/agent/handlers/main.yml
new file mode 100644
index 00000000..1921322c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/handlers/main.yml
@@ -0,0 +1,35 @@
+---
+- name: Restart Linux agent
+ service:
+ name: sensu-agent
+ state: restarted
+ when: manage_sensu_agent_service | default(False)
+
+- name: Restart Windows agent
+ action:
+ module: win_service
+ name: SensuAgent
+ state: restarted
+ when: manage_sensu_agent_service | default(False)
+
+# You probably noticed that we use some black magic in the previous handler.
+# Let us explain what it does and why did we bring it into the daylight.
+#
+# Under normal circumstances, we would write the previous handler as
+#
+# - name: Restart Windows agent
+# win_service:
+# name: SensuAgent
+# state: restarted
+#
+# When Ansible loads this handler, it makes sure it can find the win_service
+# module. And this is where things start to go downhill for us. Because we do
+# not have a guarantee that win_service module will be available (win_service
+# is not part of a certified collection), this eagerness prevents operating in
+# certain situations where win_service module is not even needed.
+#
+# And this is why we use the alternative form that forces Ansible to lazy-load
+# the module at the task/handler execution time.
+#
+# Of course, it would be much easier if we could declare ansible.windows as our
+# dependency, but this is not possible at the moment.
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/meta/argument_specs.yml b/ansible_collections/sensu/sensu_go/roles/agent/meta/argument_specs.yml
new file mode 100644
index 00000000..57e23a60
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/meta/argument_specs.yml
@@ -0,0 +1,58 @@
+argument_specs:
+
+ configure:
+ short_description: Configure Sensu Go agent
+ description:
+ - Write the Sensu Go agent configuration file.
+
+ options:
+ agent_config: &agent_config
+ description:
+ - Any option that is valid for the Sensu Go agent version we are
+ installing.
+ - All valid options are listed at
+ U(https://docs.sensu.io/sensu-go/latest/reference/agent/#configuration).
+ - Role copies the key-value pairs from the I(agent_config) variable
+ verbatim to the configuration file. This means that we must copy
+ the key names B(exactly) as they appear in the configuration
+ reference. In a way, the I(agent_config) variable should contain a
+ properly indented copy of the C(/etc/sensu/agent.yml) file.
+ type: dict
+
+ start:
+ short_description: Start Sensu Go agent
+ description:
+ - Start the Sensu Go agent service.
+
+ main:
+ short_description: Install, configure, and start Sensu Go agent
+ description:
+ - Install, configure, and start the Sensu Go agent service.
+
+ options:
+ channel:
+ description:
+ - Repository channel that serves as a source of packages.
+ - Visit the packagecloud site to find all available channels.
+ type: str
+ default: stable
+
+ version: &version
+ description:
+ - Package version to install.
+ - Can be any valid version string such as C(6.2.5) or special value
+ C(latest).
+ type: str
+ default: latest
+
+ build:
+ description:
+ - Package build to install.
+ - Can be any valid build string such as C(8290) or a special value
+ latest.
+ - If the I(version) variable is set to latest, this variable is
+ ignored and the latest available build is installed.
+ type: str
+ default: latest
+
+ agent_config: *agent_config
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/meta/main.yml b/ansible_collections/sensu/sensu_go/roles/agent/meta/main.yml
new file mode 100644
index 00000000..11d106c6
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/meta/main.yml
@@ -0,0 +1,24 @@
+galaxy_info:
+ author: XLAB Steampunk <steampunk@xlab.si>
+ description: Configure Sensu Go agent
+ license: GPL-3.0-or-later
+ min_ansible_version: 2.8
+
+ platforms:
+ - name: EL
+ versions:
+ - "7"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - trusty
+ - xenial
+ - bionic
+ - disco
+ - name: Debian
+ versions:
+ - stretch
+ - buster
+
+ galaxy_tags:
+ - sensu
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/tasks/configure.yml b/ansible_collections/sensu/sensu_go/roles/agent/tasks/configure.yml
new file mode 100644
index 00000000..5c1e35ea
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/tasks/configure.yml
@@ -0,0 +1,8 @@
+---
+- name: Configure agent (Linux)
+ include_tasks: linux/configure.yml
+ when: ansible_facts.os_family != "Windows"
+
+- name: Configure agent (Windows)
+ include_tasks: windows/configure.yml
+ when: ansible_facts.os_family == "Windows"
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/tasks/linux/configure.yml b/ansible_collections/sensu/sensu_go/roles/agent/tasks/linux/configure.yml
new file mode 100644
index 00000000..4a3db7d9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/tasks/linux/configure.yml
@@ -0,0 +1,22 @@
+---
+- name: Load Linux locations
+ include_vars: linux.yml
+
+- name: Install agent communication PKI
+ copy:
+ src: "{{ agent_trusted_ca_file }}"
+ dest: "{{ trusted_ca_file_path }}"
+ # Keep this in sync with what the backend service is running as from packager
+ owner: &sensu_user sensu
+ group: &sensu_group sensu
+ mode: "0644"
+ when: agent_trusted_ca_file is defined
+
+- name: Configure sensu-agent ({{ agent_config_path }})
+ template:
+ src: agent.yml.j2
+ dest: "{{ agent_config_path }}"
+ owner: *sensu_user
+ group: *sensu_group
+ mode: '0600'
+ notify: Restart Linux agent
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/tasks/main.yml b/ansible_collections/sensu/sensu_go/roles/agent/tasks/main.yml
new file mode 100644
index 00000000..44135ccd
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/tasks/main.yml
@@ -0,0 +1,16 @@
+---
+- name: Install sensu-go-agent binary
+ include_role:
+ name: install
+ vars:
+ components: [sensu-go-agent]
+
+- name: Inform restart handler that we are in charge of the agent service
+ set_fact:
+ manage_sensu_agent_service: true
+
+- name: Configure the agent
+ include_tasks: configure.yml
+
+- name: Start the agent
+ include_tasks: start.yml
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/tasks/start.yml b/ansible_collections/sensu/sensu_go/roles/agent/tasks/start.yml
new file mode 100644
index 00000000..7c5970a1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/tasks/start.yml
@@ -0,0 +1,15 @@
+---
+- name: Start sensu-agent (Linux)
+ service:
+ name: sensu-agent
+ state: started
+ enabled: true
+ when: ansible_facts.os_family != "Windows"
+
+- name: Start sensu-agent (Windows)
+ action:
+ module: win_service
+ name: SensuAgent
+ path: C:\Program Files\sensu\sensu-agent\bin\sensu-agent.exe service run
+ state: started
+ when: ansible_facts.os_family == "Windows"
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/tasks/windows/configure.yml b/ansible_collections/sensu/sensu_go/roles/agent/tasks/windows/configure.yml
new file mode 100644
index 00000000..e6b5f33c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/tasks/windows/configure.yml
@@ -0,0 +1,15 @@
+---
+- name: Load Windows locations
+ include_vars: windows.yml
+
+- name: Install agent communication PKI
+ win_copy:
+ src: "{{ agent_trusted_ca_file }}"
+ dest: "{{ trusted_ca_file_path }}"
+ when: agent_trusted_ca_file is defined
+
+- name: Configure sensu-agent ({{ agent_config_path }})
+ win_template:
+ src: agent.yml.j2
+ dest: "{{ agent_config_path }}"
+ notify: Restart Windows agent
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/templates/agent.yml.j2 b/ansible_collections/sensu/sensu_go/roles/agent/templates/agent.yml.j2
new file mode 100644
index 00000000..ecd55b33
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/templates/agent.yml.j2
@@ -0,0 +1,27 @@
+---
+#
+# {{ managed }}
+#
+
+##
+# Sensu agent configuration
+##
+{% if not agent_config or "backend-url" not in agent_config %}
+backend-url:
+{{ agent_backend_urls | to_nice_yaml }}
+{% endif -%}
+
+{% if agent_trusted_ca_file is defined and
+ not (agent_config and "trusted-ca-file" in agent_config) %}
+trusted-ca-file: {{ trusted_ca_file_path }}
+{% endif -%}
+
+{% if agent_trusted_ca_file is defined or agent_config and
+ ("trusted-ca-file" in agent_config and
+ not "insecure-skip-tls-verify" in agent_config) %}
+insecure-skip-tls-verify: false
+{% endif -%}
+
+{% if agent_config %}
+{{ agent_config | to_nice_yaml }}
+{% endif %}
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/vars/linux.yml b/ansible_collections/sensu/sensu_go/roles/agent/vars/linux.yml
new file mode 100644
index 00000000..66232239
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/vars/linux.yml
@@ -0,0 +1,3 @@
+---
+agent_config_path: /etc/sensu/agent.yml
+trusted_ca_file_path: /etc/sensu/sensu-agent-trusted-ca.crt
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/vars/main.yml b/ansible_collections/sensu/sensu_go/roles/agent/vars/main.yml
new file mode 100644
index 00000000..28afcd19
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/vars/main.yml
@@ -0,0 +1,2 @@
+---
+managed: Managed by Ansible - do NOT edit this file manually!
diff --git a/ansible_collections/sensu/sensu_go/roles/agent/vars/windows.yml b/ansible_collections/sensu/sensu_go/roles/agent/vars/windows.yml
new file mode 100644
index 00000000..6f422f9a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/agent/vars/windows.yml
@@ -0,0 +1,3 @@
+---
+agent_config_path: C:\ProgramData\sensu\config\agent.yml
+trusted_ca_file_path: C:\ProgramData\sensu\config\sensu-agent-trusted-ca.crt
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/README.md b/ansible_collections/sensu/sensu_go/roles/backend/README.md
new file mode 100644
index 00000000..66702076
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/README.md
@@ -0,0 +1,5 @@
+# sensu.sensu_go.backend role
+
+Visit [the official documentation site][docs] for role documentation.
+
+ [docs]: https://sensu.github.io/sensu-go-ansible/roles/backend.html
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/defaults/main.yml b/ansible_collections/sensu/sensu_go/roles/backend/defaults/main.yml
new file mode 100644
index 00000000..488a8d87
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/defaults/main.yml
@@ -0,0 +1,7 @@
+---
+cluster_admin_username: admin
+cluster_admin_password: P@ssw0rd!
+
+# Related to /etc/sensu/backend.yml, see
+# https://docs.sensu.io/sensu-go/latest/reference/backend/#configuration-summary
+backend_config:
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/handlers/main.yml b/ansible_collections/sensu/sensu_go/roles/backend/handlers/main.yml
new file mode 100644
index 00000000..f327f3f1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/handlers/main.yml
@@ -0,0 +1,6 @@
+---
+- name: Restart backend
+ service:
+ name: sensu-backend
+ state: restarted
+ when: manage_sensu_backend_service | default(False)
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/meta/argument_specs.yml b/ansible_collections/sensu/sensu_go/roles/backend/meta/argument_specs.yml
new file mode 100644
index 00000000..7f243eaf
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/meta/argument_specs.yml
@@ -0,0 +1,153 @@
+argument_specs:
+
+ configure:
+ short_description: Configure Sensu Go backend
+ description:
+ - Write the Sensu Go backend configuration file and optionally copy the
+ keys and certificates over.
+
+ options:
+ backend_config: &backend_config
+ description:
+ - Any option that is valid for the Sensu Go backend version we are
+ installing.
+ - All valid options are listed at
+ U(https://docs.sensu.io/sensu-go/latest/reference/backend/#configuration).
+ - Role copies the key-value pairs from the I(backend_config) variable
+ verbatim to the configuration file. This means that we must copy
+ the key names B(exactly) as they appear in the configuration
+ reference. In a way, the I(backend_config) variable should contain a
+ properly indented copy of the C(/etc/sensu/backend.yml) file.
+ type: dict
+
+ etcd_cert_file: &etcd_cert_file
+ description:
+ - Path to the certificate used for SSL/TLS connections B(to) etcd.
+ This is a client certificate.
+ type: str
+
+ etcd_key_file: &etcd_key_file
+ description:
+ - Path to the private key for the etcd client certificate file. Must
+ be unencrypted.
+ type: str
+
+ etcd_trusted_ca_file: &etcd_trusted_ca_file
+ description:
+ - Path to the trusted certificate authority for the etcd client
+ certificates.
+ type: str
+
+ etcd_peer_cert_file: &etcd_peer_cert_file
+ description:
+ - Path to the certificate used for SSL/TLS connections between peers.
+ This will be used both for listening on the peer address as well as
+ sending requests to other peers.
+ type: str
+
+ etcd_peer_key_file: &etcd_peer_key_file
+ description:
+ - Path to the peer certificate's key. Must be unencrypted.
+ type: str
+
+ etcd_peer_trusted_ca_file: &etcd_peer_trusted_ca_file
+ description:
+ - Path to the trusted certificate authority for the peer
+ certificates.
+ type: str
+
+ api_cert_file: &api_cert_file
+ description:
+ - Path to the certificate used to secure the Sensu Go API.
+ type: str
+
+ api_key_file: &api_key_file
+ description:
+ - Path to the private key corresponding to the Sensu Go API
+ certificate. Must be unencrypted.
+ type: str
+
+ api_trusted_ca_file: &api_trusted_ca_file
+ description:
+ - Path to the trusted certificate authority for the Sensu Go API
+ certificates.
+ type: str
+
+ dashboard_cert_file: &dashboard_cert_file
+ description:
+ - Path to the certificate used for SSL/TLS connections to the
+ dashboard.
+ type: str
+
+ dashboard_key_file: &dashboard_key_file
+ description:
+ - Path to the private key corresponding to the dashboard certificate.
+ Must be unencrypted.
+ type: str
+
+ start:
+ short_description: Start Sensu Go backend
+ description:
+ - Start the Sensu Go backend service and initialize it on the first run.
+
+ options:
+ cluster_admin_username: &cluster_admin_username
+ description:
+ - Initial admin user to create when initializing backend for the
+ first time.
+ type: str
+ default: admin
+
+ cluster_admin_password: &cluster_admin_password
+ description:
+ - Initial admin password to create when initializing backend for the
+ first time.
+ type: str
+ default: P@ssw0rd!
+
+ main:
+ short_description: Install, configure, and start Sensu Go backend
+ description:
+ - Install, configure, and start the Sensu Go backend service and
+ initialize it on the first run.
+
+ options:
+ channel:
+ description:
+ - Repository channel that serves as a source of packages.
+ - Visit the packagecloud site to find all available channels.
+ type: str
+ default: stable
+
+ version: &version
+ description:
+ - Package version to install.
+ - Can be any valid version string such as C(6.2.5) or special value
+ C(latest).
+ type: str
+ default: latest
+
+ build:
+ description:
+ - Package build to install.
+ - Can be any valid build string such as C(8290) or a special value
+ latest.
+ - If the I(version) variable is set to latest, this variable is
+ ignored and the latest available build is installed.
+ type: str
+ default: latest
+
+ backend_config: *backend_config
+ etcd_cert_file: *etcd_cert_file
+ etcd_key_file: *etcd_key_file
+ etcd_trusted_ca_file: *etcd_trusted_ca_file
+ etcd_peer_cert_file: *etcd_peer_cert_file
+ etcd_peer_key_file: *etcd_peer_key_file
+ etcd_peer_trusted_ca_file: *etcd_peer_trusted_ca_file
+ api_cert_file: *api_cert_file
+ api_key_file: *api_key_file
+ api_trusted_ca_file: *api_trusted_ca_file
+ dashboard_cert_file: *dashboard_cert_file
+ dashboard_key_file: *dashboard_key_file
+ cluster_admin_username: *cluster_admin_username
+ cluster_admin_password: *cluster_admin_password
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/meta/main.yml b/ansible_collections/sensu/sensu_go/roles/backend/meta/main.yml
new file mode 100644
index 00000000..da85fa57
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/meta/main.yml
@@ -0,0 +1,24 @@
+galaxy_info:
+ author: XLAB Steampunk <steampunk@xlab.si>
+ description: Configure Sensu Go backend
+ license: GPL-3.0-or-later
+ min_ansible_version: 2.8
+
+ platforms:
+ - name: EL
+ versions:
+ - "7"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - trusty
+ - xenial
+ - bionic
+ - disco
+ - name: Debian
+ versions:
+ - stretch
+ - buster
+
+ galaxy_tags:
+ - sensu
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/tasks/configure.yml b/ansible_collections/sensu/sensu_go/roles/backend/tasks/configure.yml
new file mode 100644
index 00000000..56102681
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/tasks/configure.yml
@@ -0,0 +1,71 @@
+---
+- name: Install etcd communication PKI
+ copy:
+ src: "{{ item.source }}"
+ dest: "/etc/sensu/{{ item.filename }}"
+ # Keep this in sync with what the backend service is running as from packager
+ owner: &sensu_user sensu
+ group: &sensu_group sensu
+ mode: "{{ item.mode | default('0644') }}"
+ loop:
+ - source: "{{ etcd_cert_file }}"
+ filename: etcd-client.crt
+ - source: "{{ etcd_key_file }}"
+ filename: etcd-client.key
+ mode: '0400'
+ - source: "{{ etcd_trusted_ca_file }}"
+ filename: etcd-client-ca.crt
+ - source: "{{ etcd_peer_cert_file }}"
+ filename: etcd-peer.crt
+ - source: "{{ etcd_peer_key_file }}"
+ filename: etcd-peer.key
+ mode: '0400'
+ - source: "{{ etcd_peer_trusted_ca_file }}"
+ filename: etcd-peer-ca.crt
+ when: etcd_trusted_ca_file is defined or etcd_cert_file is defined or
+ etcd_key_file is defined or etcd_peer_cert_file is defined or
+ etcd_peer_key_file is defined
+
+- name: Install API communication PKI
+ copy:
+ src: "{{ item.source }}"
+ dest: "/etc/sensu/{{ item.filename }}"
+ owner: *sensu_user
+ group: *sensu_group
+ mode: "{{ item.mode | default('0644') }}"
+ loop:
+ - source: "{{ api_cert_file }}"
+ filename: api.crt
+ - source: "{{ api_key_file }}"
+ filename: api.key
+ mode: '0400'
+ - source: "{{ api_trusted_ca_file }}"
+ filename: api-ca.crt
+ when: api_cert_file is defined or api_key_file is defined or
+ api_trusted_ca_file is defined
+
+- name: Install dashboard communication PKI
+ copy:
+ src: "{{ item.source }}"
+ dest: "/etc/sensu/{{ item.filename }}"
+ owner: *sensu_user
+ group: *sensu_group
+ mode: "{{ item.mode }}"
+ loop:
+ - source: "{{ dashboard_cert_file }}"
+ filename: dashboard.crt
+ mode: '0644'
+ - source: "{{ dashboard_key_file }}"
+ filename: dashboard.key
+ mode: '0400'
+ when: dashboard_cert_file is defined or dashboard_key_file is defined
+
+- name: Configure sensu-backend (/etc/sensu/backend.yml)
+ template:
+ src: backend.yml.j2
+ dest: /etc/sensu/backend.yml
+ owner: *sensu_user
+ group: *sensu_group
+ mode: '0600'
+ notify: Restart backend
+ register: configure_result
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/tasks/main.yml b/ansible_collections/sensu/sensu_go/roles/backend/tasks/main.yml
new file mode 100644
index 00000000..85de418d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/tasks/main.yml
@@ -0,0 +1,16 @@
+---
+- name: Install sensu-go-backend binary
+ include_role:
+ name: install
+ vars:
+ components: [sensu-go-backend]
+
+- name: Inform restart handler that we are in charge of the backend service
+ set_fact:
+ manage_sensu_backend_service: true
+
+- name: Configure the backend
+ include_tasks: configure.yml
+
+- name: Start the backend
+ include_tasks: start.yml
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/tasks/start.yml b/ansible_collections/sensu/sensu_go/roles/backend/tasks/start.yml
new file mode 100644
index 00000000..90a8a419
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/tasks/start.yml
@@ -0,0 +1,25 @@
+---
+- name: Start sensu-backend
+ service:
+ name: sensu-backend
+ state: started
+ enabled: true
+
+- name: Check for sensu-backend init command
+ command:
+ cmd: sensu-backend init -h
+ register: init_command_test
+ failed_when: false # Never fail, we just want to know if init exists.
+ changed_when: false # Displaying help is read-only operation.
+ check_mode: false # We do not modify the system, so we can always run
+
+- name: Initialize backend
+ command:
+ cmd: sensu-backend init
+ environment:
+ SENSU_BACKEND_CLUSTER_ADMIN_USERNAME: "{{ cluster_admin_username }}"
+ SENSU_BACKEND_CLUSTER_ADMIN_PASSWORD: "{{ cluster_admin_password }}"
+ when: init_command_test.rc == 0
+ register: init_command
+ failed_when: init_command.rc not in (0, 3) # 0 - OK, 3 - already initialized
+ changed_when: init_command.rc == 0
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/templates/backend.yml.j2 b/ansible_collections/sensu/sensu_go/roles/backend/templates/backend.yml.j2
new file mode 100644
index 00000000..2bda11eb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/templates/backend.yml.j2
@@ -0,0 +1,124 @@
+---
+#
+# {{ managed }}
+#
+
+{% if not backend_config or "state-dir" not in backend_config %}
+state-dir: "/var/lib/sensu/sensu-backend"
+{% endif -%}
+
+{% set secure_etcd = etcd_trusted_ca_file is defined or etcd_cert_file is defined or
+ etcd_key_file is defined or etcd_peer_cert_file is defined or
+ etcd_peer_key_file is defined %}
+{% if secure_etcd and
+ (not backend_config or "etcd-listen-client-urls" not in backend_config) %}
+etcd-listen-client-urls: "https://localhost:2379"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-listen-peer-urls" not in backend_config) %}
+etcd-listen-peer-urls: "https://localhost:2380"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-initial-cluster" not in backend_config) %}
+etcd-initial-cluster: "default=https://localhost:2380"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or
+ "etcd-initial-advertise-peer-urls" not in backend_config) %}
+etcd-initial-advertise-peer-urls: "https://localhost:2380"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-cert-file" not in backend_config) %}
+etcd-cert-file: "/etc/sensu/etcd-client.crt"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-key-file" not in backend_config) %}
+etcd-key-file: "/etc/sensu/etcd-client.key"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-client-cert-auth" not in backend_config) %}
+etcd-client-cert-auth: true
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-trusted-ca-file" not in backend_config) %}
+etcd-trusted-ca-file: "/etc/sensu/etcd-client-ca.crt"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-peer-cert-file" not in backend_config) %}
+etcd-peer-cert-file: "/etc/sensu/etcd-peer.crt"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or "etcd-peer-key-file" not in backend_config) %}
+etcd-peer-key-file: "/etc/sensu/etcd-peer.key"
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or
+ "etcd-peer-client-cert-auth" not in backend_config) %}
+etcd-peer-client-cert-auth: true
+{% endif -%}
+
+{% if secure_etcd and
+ (not backend_config or
+ "etcd-peer-trusted-ca-file" not in backend_config) %}
+etcd-peer-trusted-ca-file: "/etc/sensu/etcd-peer-ca.crt"
+{% endif -%}
+
+{% set secure_api = api_cert_file is defined or api_key_file is defined or
+ api_trusted_ca_file is defined %}
+{% if secure_api and
+ (not backend_config or "cert-file" not in backend_config) %}
+cert-file: "/etc/sensu/api.crt"
+{% endif -%}
+
+{% if secure_api and
+ (not backend_config or "key-file" not in backend_config) %}
+key-file: "/etc/sensu/api.key"
+{% endif -%}
+
+{% if secure_api and
+ (not backend_config or
+ "insecure-skip-tls-verify" not in backend_config) %}
+insecure-skip-tls-verify: false
+{% endif -%}
+
+{% if secure_api and
+ (not backend_config or "trusted-ca-file" not in backend_config) %}
+trusted-ca-file: "/etc/sensu/api-ca.crt"
+{% endif -%}
+
+{% if secure_api and
+ (not backend_config or
+ "insecure-skip-tls-verify" not in backend_config) %}
+insecure-skip-tls-verify: false
+{% endif -%}
+
+{% if secure_api and
+ (not backend_config or "api-url" not in backend_config) %}
+api-url: "https://localhost:8080"
+{% endif -%}
+
+{% set secure_dashboard = dashboard_cert_file is defined or
+ dashboard_key_file is defined %}
+{% if secure_dashboard and
+ (not backend_config or "dashboard-cert-file" not in backend_config) %}
+dashboard-cert-file: "/etc/sensu/dashboard.crt"
+{% endif -%}
+
+{% if secure_dashboard and
+ (not backend_config or "dashboard-key-file" not in backend_config) %}
+dashboard-key-file: "/etc/sensu/dashboard.key"
+{% endif -%}
+
+{% if backend_config %}
+{{ backend_config | to_nice_yaml }}
+{% endif %}
diff --git a/ansible_collections/sensu/sensu_go/roles/backend/vars/main.yml b/ansible_collections/sensu/sensu_go/roles/backend/vars/main.yml
new file mode 100644
index 00000000..28afcd19
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/backend/vars/main.yml
@@ -0,0 +1,2 @@
+---
+managed: Managed by Ansible - do NOT edit this file manually!
diff --git a/ansible_collections/sensu/sensu_go/roles/install/README.md b/ansible_collections/sensu/sensu_go/roles/install/README.md
new file mode 100644
index 00000000..8c76a8ce
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/README.md
@@ -0,0 +1,5 @@
+# sensu.sensu_go.install role
+
+Visit [the official documentation site][docs] for role documentation.
+
+ [docs]: https://sensu.github.io/sensu-go-ansible/roles/install.html
diff --git a/ansible_collections/sensu/sensu_go/roles/install/defaults/main.yml b/ansible_collections/sensu/sensu_go/roles/install/defaults/main.yml
new file mode 100644
index 00000000..9d2b4c1a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/defaults/main.yml
@@ -0,0 +1,12 @@
+---
+os: unknown
+dist: unknown
+
+packagecloud_auth: ""
+channel: stable
+version: latest
+build: latest
+components:
+ - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
diff --git a/ansible_collections/sensu/sensu_go/roles/install/meta/argument_specs.yml b/ansible_collections/sensu/sensu_go/roles/install/meta/argument_specs.yml
new file mode 100644
index 00000000..0ccae514
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/meta/argument_specs.yml
@@ -0,0 +1,69 @@
+argument_specs:
+
+ repositories:
+ short_description: Enable Sensu Go repos
+ description:
+ - Install required repository files on supported distributions.
+ - This entry point does not work on Windows because there is no
+ concept of repository there.
+
+ options:
+ channel: &channel
+ description:
+ - Repository channel that serves as a source of packages.
+ - Visit the packagecloud site to find all available channels.
+ type: str
+ default: stable
+
+ packages:
+ short_description: Install selected Sensu Go packages
+ description:
+ - Make sure selected packages are installed.
+ - By default, the role will install latest available package version.
+ This will change in the next major version of the collection where the
+ I(version) will become a required variable.
+
+ options:
+ components: &components
+ description:
+ - List of components to install.
+ type: list
+ elements: str
+ choices:
+ - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
+ default:
+ - sensu-go-backend
+ - sensu-go-agent
+ - sensu-go-cli
+
+ version: &version
+ description:
+ - Package version to install.
+ - Can be any valid version string such as C(6.2.5) or special value
+ C(latest).
+ type: str
+ default: latest
+
+ build: &build
+ description:
+ - Package build to install.
+ - Can be any valid build string such as C(8290) or a special value
+ latest.
+ - If the I(version) variable is set to latest, this variable is
+ ignored and the latest available build is installed.
+ type: str
+ default: latest
+
+ main:
+ short_description: Enable Sensu Go repos and install selected packages
+ description:
+ - The main entry point just combines the repositories and packages entry
+ points.
+
+ options:
+ components: *components
+ channel: *channel
+ version: *version
+ build: *build
diff --git a/ansible_collections/sensu/sensu_go/roles/install/meta/main.yml b/ansible_collections/sensu/sensu_go/roles/install/meta/main.yml
new file mode 100644
index 00000000..fbbf7b5f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/meta/main.yml
@@ -0,0 +1,24 @@
+galaxy_info:
+ author: XLAB Steampunk <steampunk@xlab.si>
+ description: Install Sensu Go components
+ license: GPL-3.0-or-later
+ min_ansible_version: 2.8
+
+ platforms:
+ - name: EL
+ versions:
+ - "7"
+ - "8"
+ - name: Ubuntu
+ versions:
+ - trusty
+ - xenial
+ - bionic
+ - disco
+ - name: Debian
+ versions:
+ - stretch
+ - buster
+
+ galaxy_tags:
+ - sensu
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/apt/install.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/apt/install.yml
new file mode 100644
index 00000000..da7f2027
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/apt/install.yml
@@ -0,0 +1,9 @@
+---
+- name: Install component
+ apt:
+ name: "{{ 'apt' | sensu.sensu_go.package_name(item, version, build) }}"
+ state: "{{ (version == 'latest') | ternary('latest', 'present') }}"
+ # FIXME(@tadeboro): This is a temporary "fix" for
+ # https://github.com/ansible/ansible/issues/29451.
+ force: true
+ loop: "{{ components }}"
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/apt/prepare.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/apt/prepare.yml
new file mode 100644
index 00000000..21daf60a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/apt/prepare.yml
@@ -0,0 +1,48 @@
+---
+- name: Include distro-specific vars ({{ ansible_distribution }})
+ include_vars: file='{{ ansible_distribution }}.yml'
+
+- name: Update apt cache (ensure we have package index)
+ apt:
+ update_cache: true
+ # Updating the APT cache does not change the system so we never report a
+ # change here (helps keep the role idempotent).
+ changed_when: false
+
+- name: Install utility packages
+ apt:
+ name:
+ - gnupg
+ - debian-archive-keyring
+ - apt-transport-https
+ state: present
+
+- name: Fetch the apt repository key
+ uri:
+ url: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/gpgkey
+ force_basic_auth: true
+ return_content: true
+ register: apt_key_download
+ # Fetching resource into memory does not change the system at all, so we
+ # never report a change here (helps keep the role idempotent). And by the
+ # same line of reasoning, we are also safe to run in check mode (the uri
+ # module does not support check mode and would cause us grief when it would
+ # be skipped).
+ changed_when: false
+ check_mode: false
+
+- name: Add apt key
+ apt_key:
+ data: "{{ apt_key_download.content }}"
+
+- name: Add apt repository
+ apt_repository:
+ repo: deb https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/{{ os }}/ {{ dist }} main
+ filename: /etc/apt/sources.list.d/sensu_{{ channel }}
+ validate_certs: true
+
+- name: Add apt source repository
+ apt_repository:
+ repo: deb-src https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/{{ os }}/ {{ dist }} main
+ filename: /etc/apt/sources.list.d/sensu_{{ channel }}
+ validate_certs: true
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/install.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/install.yml
new file mode 100644
index 00000000..5e1f02b7
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/install.yml
@@ -0,0 +1,9 @@
+---
+# Why did we kill the package-latest check? Because we really do want to be
+# able to upgrade the packages to the latest stable version.
+- name: Install component
+ dnf:
+ name: "{{ 'yum' | sensu.sensu_go.package_name(item, version, build) }}"
+ state: latest # noqa package-latest
+ allow_downgrade: true
+ loop: "{{ components }}"
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/prepare.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/prepare.yml
new file mode 100644
index 00000000..51cda371
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/dnf/prepare.yml
@@ -0,0 +1,31 @@
+---
+- name: Include distro-specific vars ({{ ansible_distribution }})
+ include_vars: file='{{ ansible_distribution }}.yml'
+
+- name: Add yum repository
+ yum_repository:
+ name: sensu_{{ channel }}
+ description: sensu_{{ channel }}
+ file: sensu
+ baseurl: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/{{ os }}/{{ dist }}/$basearch
+ gpgkey: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/gpgkey
+ gpgcheck: false
+ repo_gpgcheck: true
+ enabled: true
+ sslverify: true
+ sslcacert: /etc/pki/tls/certs/ca-bundle.crt
+ metadata_expire: '300'
+
+- name: Add yum source repository
+ yum_repository:
+ name: sensu_{{ channel }}-source
+ description: sensu_{{ channel }}-source
+ file: sensu
+ baseurl: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/{{ os }}/{{ dist }}/SRPMS
+ gpgkey: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/gpgkey
+ gpgcheck: false
+ repo_gpgcheck: true
+ enabled: true
+ sslverify: true
+ sslcacert: /etc/pki/tls/certs/ca-bundle.crt
+ metadata_expire: '300'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/main.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/main.yml
new file mode 100644
index 00000000..26d8e3b1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Prepare package repositories
+ include_tasks: repositories.yml
+ when: ansible_facts.os_family != "Windows" # No repo concept on Windows
+
+- name: Install selected packages
+ include_tasks: packages.yml
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/msi/install.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/msi/install.yml
new file mode 100644
index 00000000..5b357554
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/msi/install.yml
@@ -0,0 +1,35 @@
+---
+- name: Make sure we are installing agent
+ assert:
+ that:
+ - components | length == 1
+ - components.0 == "sensu-go-agent"
+ fail_msg: Windows hosts only support agent installation
+ quiet: true
+
+- name: Load supported agent versions on Windows
+ include_vars:
+ file: Windows.yml
+
+- name: Check if version is supported
+ assert:
+ that:
+ - version in _msi_lookup
+ fail_msg: Version {{ version }} is not supported
+ quiet: true
+
+- name: Set version, build, and arch
+ set_fact:
+ _version: "{{ _msi_lookup[version].version }}"
+ _build: "{{ _msi_lookup[version].build }}"
+ _arch: "{{ (ansible_facts.architecture == '64-bit') | ternary('x64', 'x86') }}"
+
+- name: Fetch product code
+ set_fact:
+ _product_code: "{{ _msi_lookup[_version].product_codes[_arch] }}"
+
+- name: Install component
+ win_package:
+ path: "https://s3-us-west-2.amazonaws.com/sensu.io/sensu-go/{{ _version }}\
+ /sensu-go-agent_{{ _version }}.{{ _build }}_en-US.{{ _arch }}.msi"
+ product_id: "{{ _product_code }}"
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/packages.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/packages.yml
new file mode 100644
index 00000000..73eb5153
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/packages.yml
@@ -0,0 +1,8 @@
+---
+- name: Install selected components (Linux)
+ include_tasks: "{{ ansible_pkg_mgr }}/install.yml"
+ when: ansible_facts.os_family != "Windows"
+
+- name: Install selected components (Windows)
+ include_tasks: "msi/install.yml"
+ when: ansible_facts.os_family == "Windows"
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/repositories.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/repositories.yml
new file mode 100644
index 00000000..5c32d80c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/repositories.yml
@@ -0,0 +1,3 @@
+---
+- name: Prepare package repositories
+ include_tasks: "{{ ansible_pkg_mgr }}/prepare.yml"
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/yum/install.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/yum/install.yml
new file mode 100644
index 00000000..e919c9f5
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/yum/install.yml
@@ -0,0 +1,9 @@
+---
+# Why did we kill the package-latest check? Because we really do want to be
+# able to upgrade the packages to the latest stable version.
+- name: Install component
+ yum:
+ name: "{{ 'yum' | sensu.sensu_go.package_name(item, version, build) }}"
+ state: "{{ (version == 'latest') | ternary('latest', 'present') }}"
+ allow_downgrade: true
+ loop: "{{ components }}"
diff --git a/ansible_collections/sensu/sensu_go/roles/install/tasks/yum/prepare.yml b/ansible_collections/sensu/sensu_go/roles/install/tasks/yum/prepare.yml
new file mode 100644
index 00000000..51cda371
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/tasks/yum/prepare.yml
@@ -0,0 +1,31 @@
+---
+- name: Include distro-specific vars ({{ ansible_distribution }})
+ include_vars: file='{{ ansible_distribution }}.yml'
+
+- name: Add yum repository
+ yum_repository:
+ name: sensu_{{ channel }}
+ description: sensu_{{ channel }}
+ file: sensu
+ baseurl: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/{{ os }}/{{ dist }}/$basearch
+ gpgkey: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/gpgkey
+ gpgcheck: false
+ repo_gpgcheck: true
+ enabled: true
+ sslverify: true
+ sslcacert: /etc/pki/tls/certs/ca-bundle.crt
+ metadata_expire: '300'
+
+- name: Add yum source repository
+ yum_repository:
+ name: sensu_{{ channel }}-source
+ description: sensu_{{ channel }}-source
+ file: sensu
+ baseurl: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/{{ os }}/{{ dist }}/SRPMS
+ gpgkey: https://{{ packagecloud_auth }}packagecloud.io/sensu/{{ channel }}/gpgkey
+ gpgcheck: false
+ repo_gpgcheck: true
+ enabled: true
+ sslverify: true
+ sslcacert: /etc/pki/tls/certs/ca-bundle.crt
+ metadata_expire: '300'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/Alma.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/Alma.yml
new file mode 100644
index 00000000..cb2da76e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/Alma.yml
@@ -0,0 +1,3 @@
+---
+os: el
+dist: '{{ ansible_distribution_version.split(".")[0] }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/Amazon.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/Amazon.yml
new file mode 100644
index 00000000..f760918d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/Amazon.yml
@@ -0,0 +1,3 @@
+---
+os: el
+dist: 6
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/CentOS.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/CentOS.yml
new file mode 100644
index 00000000..cb2da76e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/CentOS.yml
@@ -0,0 +1,3 @@
+---
+os: el
+dist: '{{ ansible_distribution_version.split(".")[0] }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/Debian.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/Debian.yml
new file mode 100644
index 00000000..e1dc2720
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/Debian.yml
@@ -0,0 +1,3 @@
+---
+os: debian
+dist: '{{ ansible_distribution_release }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/OracleLinux.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/OracleLinux.yml
new file mode 100644
index 00000000..cb2da76e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/OracleLinux.yml
@@ -0,0 +1,3 @@
+---
+os: el
+dist: '{{ ansible_distribution_version.split(".")[0] }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/RedHat.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/RedHat.yml
new file mode 100644
index 00000000..cb2da76e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/RedHat.yml
@@ -0,0 +1,3 @@
+---
+os: el
+dist: '{{ ansible_distribution_version.split(".")[0] }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/Rocky.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/Rocky.yml
new file mode 100644
index 00000000..cb2da76e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/Rocky.yml
@@ -0,0 +1,3 @@
+---
+os: el
+dist: '{{ ansible_distribution_version.split(".")[0] }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/Ubuntu.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/Ubuntu.yml
new file mode 100644
index 00000000..07acbc09
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/Ubuntu.yml
@@ -0,0 +1,3 @@
+---
+os: ubuntu
+dist: '{{ ansible_distribution_release }}'
diff --git a/ansible_collections/sensu/sensu_go/roles/install/vars/Windows.yml b/ansible_collections/sensu/sensu_go/roles/install/vars/Windows.yml
new file mode 100644
index 00000000..9b7101bd
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/roles/install/vars/Windows.yml
@@ -0,0 +1,314 @@
+_msi_lookup:
+ 5.20.0:
+ build: 12118
+ product_codes:
+ x64: '{1289F1F1-771F-4554-98CE-64C75EE483A0}'
+ x86: '{050A9321-E95E-4D93-8C4E-2ADE630ED6BA}'
+ version: 5.20.0
+ 5.20.1:
+ build: 12427
+ product_codes:
+ x64: '{24283517-F637-4BD7-BA6C-4E4C8A0ABC91}'
+ x86: '{CF53D755-A675-4C93-B099-850BBD9E6FFE}'
+ version: 5.20.1
+ 5.20.2:
+ build: 12959
+ product_codes:
+ x64: '{8A6B1636-A850-4F6B-B6B7-8B7BF44ABFFE}'
+ x86: '{88E35B3B-0B23-4DAC-826F-7B16B4284C89}'
+ version: 5.20.2
+ 5.21.0:
+ build: 14262
+ product_codes:
+ x64: '{072C534E-BEDF-41F3-BA24-7F57F8932E3E}'
+ x86: '{9458C181-9512-492B-8FF3-B33165D69920}'
+ version: 5.21.0
+ 5.21.1:
+ build: 25872
+ product_codes:
+ x64: '{4B391527-0345-42F7-B298-878ACA618A6C}'
+ x86: '{2F1B32C5-C6DC-4EB9-959E-B225570115DF}'
+ version: 5.21.1
+ 5.21.2:
+ build: 32982
+ product_codes:
+ x64: '{0BBB6501-40C6-4C65-A456-FBA41710B2E9}'
+ x86: '{FBE03C70-4015-48D4-8680-938E1BD03551}'
+ version: 5.21.2
+ 5.21.3:
+ build: 42094
+ product_codes:
+ x64: '{FA45624F-0296-444B-8864-BC26EC5775F1}'
+ x86: '{57F5C867-44FE-465A-8C76-AF516BBF7A83}'
+ version: 5.21.3
+ 5.21.4:
+ build: 71057
+ product_codes:
+ x64: '{63311D12-2A46-4971-80DA-3847459F6DB8}'
+ x86: '{120691DA-EB4D-4AF3-B908-192AAEBFAA26}'
+ version: 5.21.4
+ 5.21.5:
+ build: 75085
+ product_codes:
+ x64: '{04E27BF3-96B0-4DEE-A346-5EEE550422B2}'
+ x86: '{AA378A80-E188-4F7E-90B7-F98395DCCE4A}'
+ version: 5.21.5
+ 6.0.0:
+ build: 3003
+ product_codes:
+ x64: '{E0C22D5F-C434-4AEB-B44B-8972D699DEB0}'
+ x86: '{81F1520D-6FA4-4663-AA7A-599C7BC00D2D}'
+ version: 6.0.0
+ 6.1.0:
+ build: 3465
+ product_codes:
+ x64: '{D07CF7A8-741D-41D2-A4ED-DD12005F5AF6}'
+ x86: '{1C03DD78-C86E-484B-A20B-F31A81D7F7AB}'
+ version: 6.1.0
+ 6.1.1:
+ build: 3555
+ product_codes:
+ x64: '{5205A350-92F1-4643-8EAA-1B88EFCBD013}'
+ x86: '{BEB96AA7-F29B-4AF6-8C51-27CF3DA35F0B}'
+ version: 6.1.1
+ 6.1.2:
+ build: 3565
+ product_codes:
+ x64: '{FF9C0C8A-C5A0-4DC9-BE2C-BC032490E763}'
+ x86: '{4C61E49D-BE7C-4795-B00B-B400A2DEC310}'
+ version: 6.1.2
+ 6.1.3:
+ build: 3642
+ product_codes:
+ x64: '{82789B56-72CF-4AB7-81E5-C136DEF9B71A}'
+ x86: '{39658556-334B-4D48-ABC9-64B2EA7AFFEC}'
+ version: 6.1.3
+ 6.1.4:
+ build: 3866
+ product_codes:
+ x64: '{88C67A03-5C68-4BD9-9B63-EF953A3E7C3B}'
+ x86: '{D496DB94-29CB-403C-A6B3-479474DA966B}'
+ version: 6.1.4
+ 6.2.0:
+ build: 3888
+ product_codes:
+ x64: '{388D860C-287B-43D5-97F4-BF3786F9BBEE}'
+ x86: '{A85FAC85-8CB7-4F35-A2E9-0C411CC0AA87}'
+ version: 6.2.0
+ 6.2.1:
+ build: 3945
+ product_codes:
+ x64: '{58442272-4509-4F29-B010-68827FBB8AF9}'
+ x86: '{6E079E5F-5F87-4A2B-AC78-3D7A7CDEF873}'
+ version: 6.2.1
+ 6.2.2:
+ build: 3967
+ product_codes:
+ x64: '{C0EB65CB-ED89-4657-B61C-FC9B74A205D6}'
+ x86: '{94375831-C0E6-4CED-8E4A-1BCC9308A087}'
+ version: 6.2.2
+ 6.2.3:
+ build: 3986
+ product_codes:
+ x64: '{E2578FD0-6413-4368-8E50-53AFDEDB17C4}'
+ x86: '{774892E8-B9EB-4798-B833-4E28F81783CC}'
+ version: 6.2.3
+ 6.2.4:
+ build: 4013
+ product_codes:
+ x64: '{149E48FC-10C0-4D6B-8E44-3528BE7C0E71}'
+ x86: '{574B7572-FCFD-4C8F-9260-495DBB54137F}'
+ version: 6.2.4
+ 6.2.5:
+ build: 4040
+ product_codes:
+ x64: '{DC2D98AB-9490-4EC1-A002-908833DAD0A9}'
+ x86: '{0A3BB524-06DF-4EF3-815C-08899AE2BFDF}'
+ version: 6.2.5
+ 6.2.6:
+ build: 4389
+ product_codes:
+ x64: '{42807FFC-B700-481D-B4C5-3D8395C8391B}'
+ x86: '{7B1A7EA6-EB55-46B9-AB0B-A8E6C69E1EF0}'
+ version: 6.2.6
+ 6.2.7:
+ build: 4449
+ product_codes:
+ x64: '{696360AB-D47F-4426-8621-4787B70F7046}'
+ x86: '{6527ACB7-AE24-48D1-9706-15BA0022176E}'
+ version: 6.2.7
+ 6.3.0:
+ build: 4680
+ product_codes:
+ x64: '{726B92DE-0142-4C57-9A81-DDAA2B43D374}'
+ x86: '{9244B94A-9CA6-44BB-B4D9-2567A377F6B7}'
+ version: 6.3.0
+ 6.4.0:
+ build: 4826
+ product_codes:
+ x64: '{5BA3DA84-94DB-4291-A133-1C2357E18E86}'
+ x86: '{DEEF9927-C506-4340-A85B-C3D08F50C32F}'
+ version: 6.4.0
+ 6.4.1:
+ build: 4969
+ product_codes:
+ x64: '{F38A8417-D9F1-4EAE-B98E-14C7B33AB219}'
+ x86: '{8B1C0EC7-E777-4627-BDFE-27698AB21C99}'
+ version: 6.4.1
+ 6.4.2:
+ build: 5005
+ product_codes:
+ x64: '{B7C0E062-27D8-47D8-96F8-8BDD2578C5A0}'
+ x86: '{DA9B4B1C-F34D-4CD6-83BB-DA8548557746}'
+ version: 6.4.2
+ 6.4.3:
+ build: 5016
+ product_codes:
+ x64: '{D7382F5C-386B-45CB-9BFB-078E4BA8D8D4}'
+ x86: '{77A43C9E-1687-4079-BD98-AF429593762A}'
+ version: 6.4.3
+ 6.5.0:
+ build: 5266
+ product_codes:
+ x64: '{2E3DC5F9-1ADB-48CF-B1CF-1E3489E5FCF8}'
+ x86: '{B3618CC4-EBF2-416E-9A81-5E0B63454DD6}'
+ version: 6.5.0
+ 6.5.1:
+ build: 5325
+ product_codes:
+ x64: '{07149975-D964-4627-9E8B-539AF7364027}'
+ x86: '{D0AF74F6-2F34-4F71-8AF8-85A4BBFB167B}'
+ version: 6.5.1
+ 6.5.2:
+ build: 5376
+ product_codes:
+ x64: '{7F8FBFFA-D5CB-4A6A-8F9E-30BE77A96D68}'
+ x86: '{4EB2E813-73E6-4C6E-B6E5-6E6F54B3D407}'
+ version: 6.5.2
+ 6.5.3:
+ build: 5384
+ product_codes:
+ x64: '{BC81192F-CB3F-40AF-B40B-176BF3BF14FE}'
+ x86: '{92F6EBAE-ED6B-4C5A-BABF-8EC7B1EC05A5}'
+ version: 6.5.3
+ 6.5.4:
+ build: 5391
+ product_codes:
+ x64: '{F3191296-456C-4841-BD00-9C738B858435}'
+ x86: '{4FD7E30E-F441-42F1-A3BB-3C98C73DBDA4}'
+ version: 6.5.4
+ 6.5.5:
+ build: 5456
+ product_codes:
+ x64: '{6B117FCD-C3A4-43C3-94B2-19AD71D2DA16}'
+ x86: '{81EEF28C-C5BA-4F67-A24C-04B5AD534459}'
+ version: 6.5.5
+ 6.6.0:
+ build: 5502
+ product_codes:
+ x64: '{18C14F3B-A115-4CDD-A571-EFDA8ED6BFB5}'
+ x86: '{18D7558F-A977-4ECC-BA48-88E901B8ABB9}'
+ version: 6.6.0
+ 6.6.1:
+ build: 5514
+ product_codes:
+ x64: '{F9FEE5BE-91EE-45BC-AED4-B647A8E4AD08}'
+ x86: '{C4BDBBA0-4EBE-471F-BF7E-704C24C33197}'
+ version: 6.6.1
+ 6.6.2:
+ build: 5538
+ product_codes:
+ x64: '{C9AB16C4-34E5-4237-A577-8941A96AFDF7}'
+ x86: '{662D43FF-8EE5-48CB-A2FD-9F2FE1C18CBB}'
+ version: 6.6.2
+ 6.6.3:
+ build: 5588
+ product_codes:
+ x64: '{88B71CB7-6A41-4E69-BA91-D347238E21E5}'
+ x86: '{EDE79B95-0050-4F5A-97E7-9D2061D87834}'
+ version: 6.6.3
+ 6.6.4:
+ build: 5671
+ product_codes:
+ x64: '{91005E61-CA07-48F8-8CC1-01C26DDDF149}'
+ x86: '{800EDA49-0544-42D7-BD70-47CCE3E51DDB}'
+ version: 6.6.4
+ 6.6.5:
+ build: 5744
+ product_codes:
+ x64: '{C5551A82-3FC5-4657-8B3D-220DB7C3702C}'
+ x86: '{F0522F39-FA16-480F-8970-0C019020101B}'
+ version: 6.6.5
+ 6.6.6:
+ build: 5787
+ product_codes:
+ x64: '{69A99B8F-8B18-4E47-928C-BA0BDD66CEE0}'
+ x86: '{B1FEDB39-4529-44F6-BACA-2B5AEC3B663E}'
+ version: 6.6.6
+ 6.7.0:
+ build: 6196
+ product_codes:
+ x64: '{B0F70691-6B51-4DE9-91DE-F0823A4D6974}'
+ x86: '{5927FE59-DBD8-42B0-8EFA-DC9CDDED9A6E}'
+ version: 6.7.0
+ 6.7.1:
+ build: 6231
+ product_codes:
+ x64: '{02828A2A-386F-4A03-B177-F19951F17ED9}'
+ x86: '{6BF93E93-592C-4570-B160-D59BB795B093}'
+ version: 6.7.1
+ 6.7.2:
+ build: 6283
+ product_codes:
+ x64: '{03708BA6-187F-4FCF-9B3C-29F6380CACB5}'
+ x86: '{F3524177-40D4-494B-A70D-28D7B56D9341}'
+ version: 6.7.2
+ 6.7.3:
+ build: 6428
+ product_codes:
+ x64: '{69DF3F6E-15BE-43B7-A93C-A144810DDCCD}'
+ x86: '{078E09A5-9812-4554-96A3-6CE4B102F42A}'
+ version: 6.7.3
+ 6.7.4:
+ build: 6494
+ product_codes:
+ x64: '{FB58C77E-1309-4B73-9EFD-F93FBD9E8E4B}'
+ x86: '{89EB2F14-1D6F-4B8B-965C-FEFE68ADAC8B}'
+ version: 6.7.4
+ 6.7.5:
+ build: 6587
+ product_codes:
+ x64: '{1E0F61D7-7B12-4A40-935E-7275EBC90DC4}'
+ x86: '{60DF07C3-4A9A-4AE9-A3FB-00E5D5C6D88E}'
+ version: 6.7.5
+ 6.8.0:
+ build: 6692
+ product_codes:
+ x64: '{254A257F-4B06-47F1-82DF-C2291F9F4CAF}'
+ x86: '{3249DC61-714C-4330-9F8B-8051BEB230A9}'
+ version: 6.8.0
+ 6.8.1:
+ build: 6707
+ product_codes:
+ x64: '{FDB49575-1A6F-47CE-9BE5-6546ED2F496E}'
+ x86: '{8BB46F78-460D-4CD9-A0F3-FE9872D4C1EE}'
+ version: 6.8.1
+ 6.8.2:
+ build: 6788
+ product_codes:
+ x64: '{E77E67DF-46EA-4EEA-8510-BFA0C547D671}'
+ x86: '{5C066D3A-53D7-476A-B93C-588ADC38106A}'
+ version: 6.8.2
+ 6.9.0:
+ build: 6863
+ product_codes:
+ x64: '{31CC4B1A-1BF5-443C-B719-88E23D2605E3}'
+ x86: '{C2E7439E-3F4D-46AB-9275-5F313F40F30F}'
+ version: 6.9.0
+ 6.9.1: &id001
+ build: 6928
+ product_codes:
+ x64: '{1A371BB5-43CC-42E3-9294-7B5A9FED5594}'
+ x86: '{DFE77283-A52D-43AE-A156-3DAA47E72F69}'
+ version: 6.9.1
+ latest: *id001
diff --git a/ansible_collections/sensu/sensu_go/sanity.requirements b/ansible_collections/sensu/sensu_go/sanity.requirements
new file mode 100644
index 00000000..a99e2d2c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/sanity.requirements
@@ -0,0 +1,6 @@
+# ansible-lint >= 4.3.0 does not support python < 3.6 anymore
+ansible-lint==5.4.0; python_version >= "3.6"
+flake8
+yamllint
+pyyaml
+requests \ No newline at end of file
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 00000000..59769d37
--- /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 00000000..396c4ebf
--- /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 00000000..ceeb3bdc
--- /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 00000000..e69de29b
--- /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 00000000..5cc1fb19
--- /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 00000000..1648fc45
--- /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 00000000..a0867bb3
--- /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 00000000..eb550932
--- /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 00000000..a2301e4b
--- /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 00000000..1aec86d3
--- /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 00000000..f2fdc951
--- /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 00000000..efea0f67
--- /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 00000000..beff18dc
--- /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 00000000..1785c689
--- /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 00000000..1f146325
--- /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 00000000..0112074b
--- /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 00000000..e69de29b
--- /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 00000000..062ab7ee
--- /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 00000000..e69de29b
--- /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 00000000..645ebe64
--- /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 00000000..e69de29b
--- /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 00000000..d92d56f8
--- /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 00000000..e69de29b
--- /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 00000000..b771c76d
--- /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 00000000..e69de29b
--- /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 00000000..5fbb40de
--- /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 00000000..e69de29b
--- /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 00000000..a6963f97
--- /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 00000000..e69de29b
--- /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 00000000..4cca6849
--- /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 00000000..e69de29b
--- /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 00000000..4b87df9e
--- /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 00000000..e69de29b
--- /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 00000000..885698e1
--- /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 00000000..e69de29b
--- /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 00000000..6ae4f03a
--- /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 00000000..e69de29b
--- /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 00000000..5a71e7e9
--- /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 00000000..e69de29b
--- /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 00000000..160c27d6
--- /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 00000000..e69de29b
--- /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 00000000..053bdf82
--- /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 00000000..e69de29b
--- /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 00000000..52db18f8
--- /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 00000000..e69de29b
--- /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 00000000..e7f332e2
--- /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 00000000..e69de29b
--- /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 00000000..89f42f48
--- /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 00000000..e69de29b
--- /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 00000000..8fe9f068
--- /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 00000000..e69de29b
--- /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 00000000..b1871e70
--- /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 00000000..e69de29b
--- /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 00000000..25f79676
--- /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 00000000..e69de29b
--- /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 00000000..cbb02fee
--- /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 00000000..e69de29b
--- /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 00000000..db34cca9
--- /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 00000000..e69de29b
--- /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 00000000..a5971afc
--- /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 00000000..ceffe521
--- /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 00000000..b6c5fe9f
--- /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 00000000..d22f6871
--- /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 00000000..e69de29b
--- /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 00000000..20496d0e
--- /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 00000000..e69de29b
--- /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 00000000..8dcb5fc1
--- /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 00000000..e69de29b
--- /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 00000000..a06da305
--- /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 00000000..e69de29b
--- /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 00000000..7312779e
--- /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 00000000..e69de29b
--- /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 00000000..f00ffa71
--- /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 00000000..7ba0b239
--- /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 00000000..40ba6223
--- /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 00000000..f56054f0
--- /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 00000000..505b2263
--- /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 00000000..bbe82156
--- /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 00000000..7e0e1ccf
--- /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 00000000..fe9c08f4
--- /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 00000000..430054e8
--- /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 00000000..05699844
--- /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 00000000..7ba0b239
--- /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 00000000..40ba6223
--- /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 00000000..69becbbf
--- /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 00000000..f9c28ce1
--- /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 00000000..c6d487ea
--- /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 00000000..fd686187
--- /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 00000000..fe9c08f4
--- /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 00000000..85b70556
--- /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 00000000..1bed38d5
--- /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 00000000..fe9c08f4
--- /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 00000000..90e86640
--- /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 00000000..3d3dfd47
--- /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 00000000..fe9c08f4
--- /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 00000000..68363428
--- /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 00000000..e5854027
--- /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 00000000..8cc61dad
--- /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 00000000..da9a9f95
--- /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 00000000..9d526ef3
--- /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 00000000..40ba6223
--- /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 00000000..0aa2bf12
--- /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 00000000..59e497b6
--- /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 00000000..70be5856
--- /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 00000000..59e497b6
--- /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 00000000..7ac2dd83
--- /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 00000000..1261bc9d
--- /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 00000000..8dadfc5e
--- /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 00000000..7ac2dd83
--- /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 00000000..a0d8b5e6
--- /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 00000000..8dadfc5e
--- /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 00000000..b498ee94
--- /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 00000000..c276ce55
--- /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 00000000..af9fcfb5
--- /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 00000000..af9fcfb5
--- /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 00000000..af9fcfb5
--- /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 00000000..af9fcfb5
--- /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 00000000..af9fcfb5
--- /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 00000000..af9fcfb5
--- /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 00000000..5a7ce765
--- /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 00000000..b8544ff9
--- /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 00000000..100b30c6
--- /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 00000000..261080e3
--- /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 00000000..794727ab
--- /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 00000000..ae62f1f5
--- /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 00000000..cf0d8a49
--- /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 00000000..ecc6fc54
--- /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 00000000..84fddb53
--- /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 00000000..e4f6ff57
--- /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 00000000..f737dce0
--- /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 00000000..2287dbc9
--- /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 00000000..a7204bb5
--- /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 00000000..8e9298b2
--- /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 00000000..aab09b57
--- /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 00000000..bd19bceb
--- /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 00000000..e935a7a9
--- /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 00000000..8ea6ecb9
--- /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 00000000..53ed5b7c
--- /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 00000000..28993364
--- /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 00000000..45158dcd
--- /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 00000000..48e939a0
--- /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 00000000..b18c2d22
--- /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 00000000..34e58451
--- /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 00000000..11eadb6a
--- /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 00000000..665b29ab
--- /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 00000000..4a1efc9c
--- /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 00000000..fb098ed8
--- /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 00000000..964dcaed
--- /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 00000000..982c97c5
--- /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 00000000..e651dab7
--- /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 00000000..8808ec2c
--- /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 00000000..c9270892
--- /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 00000000..f92a5262
--- /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 00000000..d8bbcf41
--- /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 00000000..bd6aa805
--- /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 00000000..3b6e6da8
--- /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 00000000..82269840
--- /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 00000000..4b9e1c46
--- /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 00000000..9cef32e7
--- /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 00000000..b1456c8f
--- /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 00000000..e51a63af
--- /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 00000000..431b6463
--- /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 00000000..0780cdc2
--- /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 00000000..69c3254a
--- /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 00000000..2339e855
--- /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 00000000..cd1ce4fb
--- /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 00000000..b22eaa7e
--- /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 00000000..2f772516
--- /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 00000000..a2bec291
--- /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 00000000..5912ae73
--- /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 00000000..161204f2
--- /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 00000000..cce081bf
--- /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 00000000..69515249
--- /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 00000000..fc4c17a8
--- /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 00000000..ad064271
--- /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 00000000..7a221541
--- /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 00000000..b4c72f19
--- /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 00000000..3eb2796b
--- /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 00000000..52e4a769
--- /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 00000000..09fc337f
--- /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 00000000..7f0b6e75
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt
@@ -0,0 +1 @@
+bcrypt
diff --git a/ansible_collections/sensu/sensu_go/tools/windows-versions.py b/ansible_collections/sensu/sensu_go/tools/windows-versions.py
new file mode 100755
index 00000000..1706a0a0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tools/windows-versions.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import argparse
+import gzip
+import pathlib
+import shutil
+import subprocess
+import sys
+
+from urllib import request
+from xml.etree import ElementTree
+
+import yaml
+
+
+BASE_REPO_URL = "https://packagecloud.io/sensu/stable/el/8/x86_64/"
+FILENAME_TEMPLATE = "sensu-go-agent_{0}.{1}.{2}.{3}_en-US.{arch}.msi"
+DOWNLOAD_URL_TEMPLATE = (
+ "https://s3-us-west-2.amazonaws.com/sensu.io/sensu-go/{0}.{1}.{2}/" +
+ FILENAME_TEMPLATE
+)
+MINIMAL_VERSION = (5, 20, 0)
+
+
+class ArgParser(argparse.ArgumentParser):
+ """An argument parser that displays help on error."""
+
+ def error(self, message):
+ sys.stderr.write("error: {0}\n".format(message))
+ self.print_help()
+ sys.exit(2)
+
+ def add_subparsers(self, **kwargs):
+ # Workaround for http://bugs.python.org/issue9253
+ subparsers = super(ArgParser, self).add_subparsers()
+ subparsers.required = True
+ subparsers.dest = "command"
+ return subparsers
+
+
+def _fetch_available_versions():
+ available_versions = set()
+
+ response = request.urlopen(BASE_REPO_URL + "repodata/repomd.xml", timeout=30)
+ root = ElementTree.parse(response).getroot()
+ for data in root.iter("{http://linux.duke.edu/metadata/repo}data"):
+ if data.get("type") == "primary":
+ break
+ else:
+ return available_versions
+
+ location = next(data.iter("{http://linux.duke.edu/metadata/repo}location"))
+ path = location.attrib["href"]
+
+ response = request.urlopen(BASE_REPO_URL + path, timeout=30)
+ root = ElementTree.fromstring(gzip.decompress(response.read()))
+ for package in root.iter("{http://linux.duke.edu/metadata/common}package"):
+ name = next(package.iter("{http://linux.duke.edu/metadata/common}name"))
+ if name.text != "sensu-go-agent":
+ continue
+
+ version = next(package.iter("{http://linux.duke.edu/metadata/common}version"))
+ version_tuple = tuple(int(c) for c in version.get("ver").split("."))
+ if version_tuple < MINIMAL_VERSION:
+ continue
+
+ available_versions.add(version_tuple + (int(version.get("rel")), ))
+
+ return available_versions
+
+
+def _load_versions_from_vars(vars):
+ return set(
+ (tuple(int(c) for c in item["version"].split(".")) + (item["build"],))
+ for item in vars["_msi_lookup"].values()
+ )
+
+
+def _sync_versions(vars, available_versions, cache_dir):
+ new_vars = dict(vars, _msi_lookup={})
+
+ old_msis = vars["_msi_lookup"]
+ new_msis = new_vars["_msi_lookup"]
+
+ cache = pathlib.Path(cache_dir)
+
+ for version in sorted(available_versions):
+ version_str = ".".join(map(str, version[:3]))
+
+ if version_str in old_msis:
+ # Happy path: we already have this version sorted
+ new_msis[version_str] = old_msis[version_str]
+ continue
+
+ # Sad path: we need to download packages and extract product codes
+ product_codes = {}
+ for arch in ("x86", "x64"):
+ url = DOWNLOAD_URL_TEMPLATE.format(*version, arch=arch)
+ filename = FILENAME_TEMPLATE.format(*version, arch=arch)
+ file = cache / filename
+
+ if not file.is_file():
+ print("Downloading " + filename)
+ with open(file, "wb") as fp:
+ response = request.urlopen(url)
+ shutil.copyfileobj(response, fp)
+ else:
+ print("Reusing " + filename)
+
+ process = subprocess.run(
+ ("msiinfo", "export", str(file), "Property"), capture_output=True,
+ check=True
+ )
+ for line in process.stdout.splitlines():
+ field, value = line.split(b"\t")
+ if field == b"ProductCode":
+ product_codes[arch] = value.decode("ascii")
+
+ new_msis[version_str] = dict(
+ version=version_str, build=version[-1], product_codes=product_codes
+ )
+
+ new_msis["latest"] = new_msis[version_str]
+
+ return new_vars
+
+
+def _load_windows_vars_file(filename):
+ with open(filename, "r") as fd:
+ return yaml.safe_load(fd)
+
+
+def _save_windows_vars_file(filename, vars):
+ with open(filename, "w") as fd:
+ yaml.safe_dump(vars, fd)
+
+
+def _check(args):
+ vars_data = _load_windows_vars_file(args.vars)
+ current = _load_versions_from_vars(vars_data)
+ available = _fetch_available_versions()
+
+ missing = available - current
+ obsolete = current - available
+
+ if missing:
+ print("The following versions are missing: {0}".format(
+ ", ".join(".".join(map(str, v)) for v in missing)
+ ))
+ if obsolete:
+ print("The following versions are obsolete: {0}".format(
+ ", ".join(".".join(map(str, v)) for v in obsolete)
+ ))
+
+ return len(missing) + len(obsolete)
+
+
+def _update(args):
+ vars_data = _load_windows_vars_file(args.vars)
+ current = _load_versions_from_vars(vars_data)
+ available = _fetch_available_versions()
+
+ if current == available:
+ return 0
+
+ new_vars_data = _sync_versions(vars_data, available, args.cache)
+ _save_windows_vars_file(args.vars, new_vars_data)
+
+ return 0
+
+
+def main():
+ parser = ArgParser(
+ description="Windows agent version updater",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter
+ )
+ subparsers = parser.add_subparsers()
+
+ check = subparsers.add_parser("check", help="Check for version updates")
+ check.add_argument("vars", help="Variable file with Windows lookup table")
+ check.set_defaults(func=_check)
+
+ update = subparsers.add_parser("update", help="Update lookup table")
+ update.add_argument("vars", help="Variable file with Windows lookup table")
+ update.add_argument(
+ "--cache", help="Directory used for caching downloads", default="/tmp"
+ )
+ update.set_defaults(func=_update)
+
+ args = parser.parse_args()
+
+ return args.func(args)
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/ansible_collections/sensu/sensu_go/vagrant/windows/Vagrantfile b/ansible_collections/sensu/sensu_go/vagrant/windows/Vagrantfile
new file mode 100644
index 00000000..ebc89336
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/vagrant/windows/Vagrantfile
@@ -0,0 +1,15 @@
+Vagrant.configure("2") do |config|
+ config.vm.define "backend" do |backend|
+ backend.vm.box = "centos/8"
+ backend.vm.network "private_network", ip: "192.168.50.91"
+ backend.vm.provision "ansible" do |ansible|
+ ansible.playbook = "back.yaml"
+ end
+ end
+
+ config.vm.define "agent" do |agent|
+ agent.vm.box = "gusztavvargadr/windows-10"
+ agent.vm.hostname = "host-win"
+ agent.vm.network "private_network", ip: "192.168.50.90"
+ end
+end
diff --git a/ansible_collections/sensu/sensu_go/vagrant/windows/back.yaml b/ansible_collections/sensu/sensu_go/vagrant/windows/back.yaml
new file mode 100644
index 00000000..8ede29ba
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/vagrant/windows/back.yaml
@@ -0,0 +1,18 @@
+---
+- name: Install backend
+ hosts: all
+ become: true
+
+ tasks:
+ - name: Install cli
+ include_role:
+ name: sensu.sensu_go.install
+ vars:
+ version: latest
+ components: [ sensu-go-cli ]
+
+ - name: Install backend
+ include_role:
+ name: sensu.sensu_go.backend
+ vars:
+ version: latest
diff --git a/ansible_collections/sensu/sensu_go/vagrant/windows/inventory.yaml b/ansible_collections/sensu/sensu_go/vagrant/windows/inventory.yaml
new file mode 100644
index 00000000..8cee9953
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/vagrant/windows/inventory.yaml
@@ -0,0 +1,10 @@
+---
+all:
+ hosts:
+ windows:
+ ansible_host: 192.168.50.90
+ ansible_port: 5985
+ ansible_user: vagrant
+ ansible_password: vagrant
+ ansible_connection: winrm
+ ansible_winrm_transport: basic
diff --git a/ansible_collections/sensu/sensu_go/vagrant/windows/play.yaml b/ansible_collections/sensu/sensu_go/vagrant/windows/play.yaml
new file mode 100644
index 00000000..fcf2137d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/vagrant/windows/play.yaml
@@ -0,0 +1,14 @@
+---
+- name: Install windows agent
+ hosts: windows
+
+ tasks:
+ - name: Install agent
+ include_role:
+ name: sensu.sensu_go.agent
+ vars:
+ version: latest
+ agent_config:
+ name: win
+ backend-url:
+ - ws://192.168.50.91:8081