summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/consul_agent_check.py
blob: 373926004971b7ff4fb38d1719ac1d6b31e331c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2024, Michael Ilg
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = '''
module: consul_agent_check
short_description: Add, modify, and delete checks within a consul cluster
version_added: 9.1.0
description:
 - Allows the addition, modification and deletion of checks in a consul
   cluster via the agent. For more details on using and configuring Checks,
   see U(https://developer.hashicorp.com/consul/api-docs/agent/check).
 - Currently, there is no complete way to retrieve the script, interval or TTL
   metadata for a registered check. Without this metadata it is not possible to
   tell if the data supplied with ansible represents a change to a check. As a
   result this does not attempt to determine changes and will always report a
   changed occurred. An API method is planned to supply this metadata so at that
   stage change management will be added.
author:
  - Michael Ilg (@Ilgmi)
extends_documentation_fragment:
  - community.general.consul
  - community.general.consul.actiongroup_consul
  - community.general.consul.token
  - community.general.attributes
attributes:
  check_mode:
    support: full
    details:
      - The result is the object as it is defined in the module options and not the object structure of the consul API.
        For a better overview of what the object structure looks like,
        take a look at U(https://developer.hashicorp.com/consul/api-docs/agent/check#list-checks).
  diff_mode:
    support: partial
    details:
      - In check mode the diff will show the object as it is defined in the module options and not the object structure of the consul API.
options:
  state:
    description:
      - Whether the check should be present or absent.
    choices: ['present', 'absent']
    default: present
    type: str
  name:
    description:
    - Required name for the service check.
    type: str
  id:
    description:
      - Specifies a unique ID for this check on the node. This defaults to the O(name) parameter, but it may be necessary to provide
        an ID for uniqueness. This value will return in the response as "CheckId".
    type: str
  interval:
    description:
      - The interval at which the service check will be run.
        This is a number with a V(s) or V(m) suffix to signify the units of seconds or minutes, for example V(15s) or V(1m).
        If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
      - Required if one of the parameters O(args), O(http), or O(tcp) is specified.
    type: str
  notes:
    description:
      - Notes to attach to check when registering it.
    type: str
  args:
    description:
      - Specifies command arguments to run to update the status of the check.
      - Requires O(interval) to be provided.
      - Mutually exclusive with O(ttl), O(tcp) and O(http).
    type: list
    elements: str
  ttl:
    description:
      - Checks can be registered with a TTL instead of a O(args) and O(interval)
        this means that the service will check in with the agent before the
        TTL expires. If it doesn't the check will be considered failed.
        Required if registering a check and the script an interval are missing
        Similar to the interval this is a number with a V(s) or V(m) suffix to
        signify the units of seconds or minutes, for example V(15s) or V(1m).
        If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
      - Mutually exclusive with O(args), O(tcp) and O(http).
    type: str
  tcp:
    description:
      - Checks can be registered with a TCP port. This means that consul
        will check if the connection attempt to that port is successful (that is, the port is currently accepting connections).
        The format is V(host:port), for example V(localhost:80).
      - Requires O(interval) to be provided.
      - Mutually exclusive with O(args), O(ttl) and O(http).
    type: str
    version_added: '1.3.0'
  http:
    description:
      - Checks can be registered with an HTTP endpoint. This means that consul
        will check that the http endpoint returns a successful HTTP status.
      - Requires O(interval) to be provided.
      - Mutually exclusive with O(args), O(ttl) and O(tcp).
    type: str
  timeout:
    description:
      - A custom HTTP check timeout. The consul default is 10 seconds.
        Similar to the interval this is a number with a V(s) or V(m) suffix to
        signify the units of seconds or minutes, for example V(15s) or V(1m).
        If no suffix is supplied V(s) will be used by default, for example V(10) will be V(10s).
    type: str
  service_id:
    description:
      - The ID for the service, must be unique per node. If O(state=absent),
        defaults to the service name if supplied.
    type: str
'''

EXAMPLES = '''
- name: Register tcp check for service 'nginx'
  community.general.consul_agent_check:
    name: nginx_tcp_check
    service_id: nginx
    interval: 60s
    tcp: localhost:80
    notes: "Nginx Check"

- name: Register http check for service 'nginx'
  community.general.consul_agent_check:
    name: nginx_http_check
    service_id: nginx
    interval: 60s
    http: http://localhost:80/status
    notes: "Nginx Check"

- name: Remove check for service 'nginx'
  community.general.consul_agent_check:
    state: absent
    id: nginx_http_check
    service_id: "{{ nginx_service.ID }}"
'''

RETURN = """
check:
    description: The check as returned by the consul HTTP API.
    returned: always
    type: dict
    sample:
        CheckID: nginx_check
        ServiceID: nginx
        Interval: 30s
        Type: http
        Notes: Nginx Check
operation:
    description: The operation performed.
    returned: changed
    type: str
    sample: update
"""

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.consul import (
    AUTH_ARGUMENTS_SPEC,
    OPERATION_CREATE,
    OPERATION_UPDATE,
    OPERATION_DELETE,
    OPERATION_READ,
    _ConsulModule,
    validate_check,
)

_ARGUMENT_SPEC = {
    "state": dict(default="present", choices=["present", "absent"]),
    "name": dict(type='str'),
    "id": dict(type='str'),
    "interval": dict(type='str'),
    "notes": dict(type='str'),
    "args": dict(type='list', elements='str'),
    "http": dict(type='str'),
    "tcp": dict(type='str'),
    "ttl": dict(type='str'),
    "timeout": dict(type='str'),
    "service_id": dict(type='str'),
}

_MUTUALLY_EXCLUSIVE = [
    ('args', 'ttl', 'tcp', 'http'),
]

_REQUIRED_IF = [
    ('state', 'present', ['name']),
    ('state', 'absent', ('id', 'name'), True),
]

_REQUIRED_BY = {
    'args': 'interval',
    'http': 'interval',
    'tcp': 'interval',
}

_ARGUMENT_SPEC.update(AUTH_ARGUMENTS_SPEC)


class ConsulAgentCheckModule(_ConsulModule):
    api_endpoint = "agent/check"
    result_key = "check"
    unique_identifiers = ["id", "name"]
    operational_attributes = {"Node", "CheckID", "Output", "ServiceName", "ServiceTags",
                              "Status", "Type", "ExposedPort", "Definition"}

    def endpoint_url(self, operation, identifier=None):
        if operation == OPERATION_READ:
            return "agent/checks"
        if operation in [OPERATION_CREATE, OPERATION_UPDATE]:
            return "/".join([self.api_endpoint, "register"])
        if operation == OPERATION_DELETE:
            return "/".join([self.api_endpoint, "deregister", identifier])

        return super(ConsulAgentCheckModule, self).endpoint_url(operation, identifier)

    def read_object(self):
        url = self.endpoint_url(OPERATION_READ)
        checks = self.get(url)
        identifier = self.id_from_obj(self.params)
        if identifier in checks:
            return checks[identifier]
        return None

    def prepare_object(self, existing, obj):
        existing = super(ConsulAgentCheckModule, self).prepare_object(existing, obj)
        validate_check(existing)
        return existing

    def delete_object(self, obj):
        if not self._module.check_mode:
            self.put(self.endpoint_url(OPERATION_DELETE, obj.get("CheckID")))
        return {}


def main():
    module = AnsibleModule(
        _ARGUMENT_SPEC,
        mutually_exclusive=_MUTUALLY_EXCLUSIVE,
        required_if=_REQUIRED_IF,
        required_by=_REQUIRED_BY,
        supports_check_mode=True,
    )

    consul_module = ConsulAgentCheckModule(module)
    consul_module.execute()


if __name__ == "__main__":
    main()