summaryrefslogtreecommitdiffstats
path: root/ansible_collections/sensu/sensu_go/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/sensu/sensu_go/plugins')
-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
69 files changed, 8990 insertions, 0 deletions
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 000000000..74473aeb7
--- /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 000000000..3e93c9484
--- /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 000000000..4fe8aee04
--- /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 000000000..326143684
--- /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 000000000..849cd85df
--- /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 000000000..68efc27a8
--- /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 000000000..169a1ee0b
--- /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 000000000..15ca542bc
--- /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 000000000..44c48145b
--- /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 000000000..513c20650
--- /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 000000000..ecafd458f
--- /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 000000000..80e9a8750
--- /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 000000000..aed632dcf
--- /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 000000000..b7962cf85
--- /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 000000000..cd53077d3
--- /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 000000000..25bee3887
--- /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 000000000..5be0675c7
--- /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 000000000..054fa7663
--- /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 000000000..4079473fb
--- /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 000000000..bf97120f1
--- /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 000000000..609b16cb9
--- /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 000000000..627bfabb3
--- /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 000000000..3c128dcb4
--- /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 000000000..6215af445
--- /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 000000000..417218ec7
--- /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 000000000..47f446936
--- /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 000000000..3bb207c91
--- /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 000000000..a75fbbe27
--- /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 000000000..0fb64c270
--- /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 000000000..20aecb54f
--- /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 000000000..de60d0252
--- /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 000000000..8d46fdc99
--- /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 000000000..e2374dff8
--- /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 000000000..706f3d1d8
--- /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 000000000..b31c6481a
--- /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 000000000..e8d1562d9
--- /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 000000000..511dd2372
--- /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 000000000..143aca8f0
--- /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 000000000..cf58d7c8b
--- /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 000000000..ae2ae333d
--- /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 000000000..90a4d84ff
--- /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 000000000..e8023b436
--- /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 000000000..bdad1fa56
--- /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 000000000..600de0a1d
--- /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 000000000..aedc9856c
--- /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 000000000..b78f90da5
--- /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 000000000..f576a5e4f
--- /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 000000000..e0ee4da04
--- /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 000000000..0baf8ca8c
--- /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 000000000..1e2472056
--- /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 000000000..64d174cb6
--- /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 000000000..d5d0a2690
--- /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 000000000..bcd5d4ed3
--- /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 000000000..99e985f7e
--- /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 000000000..54f58d131
--- /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 000000000..0bab8b1bc
--- /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 000000000..ef7d230f8
--- /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 000000000..cf2c7e8ff
--- /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 000000000..6b0125578
--- /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 000000000..a16ca82a3
--- /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 000000000..258b6db09
--- /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 000000000..b3aa1b0e3
--- /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 000000000..2df0a3108
--- /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 000000000..4b2879117
--- /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 000000000..68769902a
--- /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 000000000..ac42d27c3
--- /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 000000000..ba18f518f
--- /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 000000000..4ef155867
--- /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 000000000..ad9eda8fb
--- /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()