Skip to content

Commit

Permalink
Merge branch 'main' into feature/contact-points
Browse files Browse the repository at this point in the history
  • Loading branch information
Nemental authored May 27, 2024
2 parents 5478f19 + 2a031d0 commit 9dd2987
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 97 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/371-silence-org-switch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- add org switch by `org_id` and `org_name` in `grafana_silence`
50 changes: 43 additions & 7 deletions plugins/modules/grafana_silence.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@
requirements:
- The Alertmanager API is only available starting Grafana 8 and the module will fail if the server version is lower than version 8.
options:
org_id:
description:
- The Grafana organization ID where the silence will be created or deleted.
- Not used when I(grafana_api_key) is set, because the grafana_api_key only belongs to one organization.
- Mutually exclusive with C(org_name).
default: 1
type: int
org_name:
description:
- The Grafana organization name where the silence will be created or deleted.
- Not used when I(grafana_api_key) is set, because the grafana_api_key only belongs to one organization.
- Mutually exclusive with C(org_id).
type: str
comment:
description:
- The comment that describes the silence.
Expand Down Expand Up @@ -181,9 +194,10 @@ class GrafanaError(Exception):
class GrafanaSilenceInterface(object):
def __init__(self, module):
self._module = module
self.grafana_url = base.clean_url(module.params.get("url"))
self.org_id = None
# {{{ Authentication header
self.headers = {"Content-Type": "application/json"}
module.params["force_basic_auth"] = True
if module.params.get("grafana_api_key", None):
self.headers["Authorization"] = (
"Bearer %s" % module.params["grafana_api_key"]
Expand All @@ -192,8 +206,14 @@ def __init__(self, module):
self.headers["Authorization"] = basic_auth_header(
module.params["url_username"], module.params["url_password"]
)
self.org_id = (
self.organization_by_name(module.params["org_name"])
if module.params["org_name"]
else module.params["org_id"]
)
self.switch_organization(self.org_id)
# }}}
self.grafana_url = base.clean_url(module.params.get("url"))

if module.params.get("skip_version_check") is False:
try:
grafana_version = self.get_version()
Expand Down Expand Up @@ -233,6 +253,21 @@ def _send_request(self, url, data=None, headers=None, method="GET"):
failed=True, msg="Grafana Silences API answered with HTTP %d" % status_code
)

def switch_organization(self, org_id):
url = "/api/user/using/%d" % org_id
self._send_request(url, headers=self.headers, method="POST")

def organization_by_name(self, org_name):
url = "/api/user/orgs"
organizations = self._send_request(url, headers=self.headers, method="GET")
orga = next((org for org in organizations if org["name"] == org_name))
if orga:
return orga["orgId"]

return self._module.fail_json(
failed=True, msg="Current user isn't member of organization: %s" % org_name
)

def get_version(self):
url = "/api/health"
response = self._send_request(
Expand Down Expand Up @@ -310,23 +345,24 @@ def setup_module_object():
argument_spec = base.grafana_argument_spec()
argument_spec.update(
comment=dict(type="str", required=True),
state=dict(type="str", choices=["present", "absent"], default="present"),
created_by=dict(type="str", required=True),
starts_at=dict(type="str", required=True),
ends_at=dict(type="str", required=True),
matchers=dict(type="list", elements="dict", required=True),
org_id=dict(default=1, type="int"),
org_name=dict(type="str"),
skip_version_check=dict(type="bool", default=False),
starts_at=dict(type="str", required=True),
state=dict(type="str", choices=["present", "absent"], default="present"),
)


def main():

module = setup_module_object()
comment = module.params["comment"]
created_by = module.params["created_by"]
starts_at = module.params["starts_at"]
ends_at = module.params["ends_at"]
matchers = module.params["matchers"]
starts_at = module.params["starts_at"]
state = module.params["state"]

changed = False
Expand All @@ -338,7 +374,6 @@ def main():
)

if state == "present":

if not silence:
silence = grafana_iface.create_silence(
comment, created_by, starts_at, ends_at, matchers
Expand All @@ -362,6 +397,7 @@ def main():
changed=changed,
msg="Silence does not exist",
)

module.exit_json(failed=failed, changed=changed, silence=silence)


Expand Down
2 changes: 2 additions & 0 deletions roles/grafana/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ Configure Grafana organizations, dashboards, folders, datasources, teams and use
| created_by | yes |
| ends_at | yes |
| matchers | yes |
| org_id | no |
| org_name | no |
| starts_at | yes |
| state | no |
| [**grafana_contact_point**](https://docs.ansible.com/ansible/latest/collections/community/grafana/grafana_contact_point_module.html) |
Expand Down
4 changes: 3 additions & 1 deletion roles/grafana/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,11 @@
community.grafana.grafana_silence:
comment: "{{ silence.comment }}"
created_by: "{{ silence.created_by }}"
starts_at: "{{ silence.starts_at }}"
ends_at: "{{ silence.ends_at }}"
matchers: "{{ silence.matchers }}"
org_id: "{{ datasource.org_id | default(omit) }}"
org_name: "{{ datasource.org_name | default(omit) }}"
starts_at: "{{ silence.starts_at }}"
state: "{{ silence.state | default(omit) }}"
loop: "{{ grafana_silences }}"
loop_control: {loop_var: silence}
Expand Down
83 changes: 83 additions & 0 deletions tests/integration/targets/grafana_silence/tasks/create-delete.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
- module_defaults:
community.grafana.grafana_silence:
url: "{{ grafana_url }}"
url_username: "{{ grafana_username }}"
url_password: "{{ grafana_password }}"
block:
- name: Create new silence
community.grafana.grafana_silence:
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: present
register: result
- assert:
that:
- "result.changed == true"
- "result.failed == false"
- "result.silence.id != ''"

- name: Check idempotency on silence creation
community.grafana.grafana_silence:
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: present
register: result
- assert:
that:
- "result.changed == false"
- "result.msg != ''"

- name: Delete the silence
community.grafana.grafana_silence:
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: absent
register: result
- assert:
that:
- "result.changed == true"
- "result.failed == false"
- "result.silence.id != ''"
- - "result.silence.createdBy != 'me'"

- name: Check idempotency on silence deletion
community.grafana.grafana_silence:
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: absent
register: result
ignore_errors: yes
- assert:
that:
- "result.changed == false"
- "result.failed == false"
- "result.msg == 'Silence does not exist'"
91 changes: 4 additions & 87 deletions tests/integration/targets/grafana_silence/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,89 +1,6 @@
---
- name: Create new silence
community.grafana.grafana_silence:
url: "{{ grafana_url }}"
url_username: "{{ grafana_username }}"
url_password: "{{ grafana_password }}"
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: present
register: result
- assert:
that:
- "result.changed == true"
- "result.failed == false"
- "result.silence.id != ''"
- name: Silence creation and deletion
ansible.builtin.include_tasks: create-delete.yml

- name: Check idempotency on silence creation
community.grafana.grafana_silence:
url: "{{ grafana_url }}"
url_username: "{{ grafana_username }}"
url_password: "{{ grafana_password }}"
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: present
register: result
- assert:
that:
- "result.changed == false"
- "result.msg != ''"

- name: Delete the silence
community.grafana.grafana_silence:
url: "{{ grafana_url }}"
url_username: "{{ grafana_username }}"
url_password: "{{ grafana_password }}"
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: absent
register: result
- assert:
that:
- "result.changed == true"
- "result.failed == false"
- "result.silence.id != ''"
- - "result.silence.createdBy != 'me'"

- name: Check idempotency on silence deletion
community.grafana.grafana_silence:
url: "{{ grafana_url }}"
url_username: "{{ grafana_username }}"
url_password: "{{ grafana_password }}"
comment: "a testcomment"
created_by: "me"
starts_at: "2029-07-29T08:45:45.000Z"
ends_at: "2029-07-29T08:55:45.000Z"
matchers:
- isEqual: true
isRegex: true
name: environment
value: test
state: absent
register: result
ignore_errors: yes
- assert:
that:
- "result.changed == false"
- "result.failed == false"
- "result.msg == 'Silence does not exist'"
- name: Silence creation and deletion for organization
ansible.builtin.include_tasks: org.yml
7 changes: 7 additions & 0 deletions tests/integration/targets/grafana_silence/tasks/org.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
- module_defaults:
community.grafana.grafana_silence:
org_name: Main Org.
block:
- name: Silence creation and deletion
ansible.builtin.include_tasks: create-delete.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def test_create_silence_new_silence(
}
],
)
mock_fetch_url.assert_called_once_with(
mock_fetch_url.assert_called_with(
module,
"https://grafana.example.com/api/alertmanager/grafana/api/v2/silences",
data=json.dumps(
Expand Down Expand Up @@ -198,7 +198,7 @@ def test_delete_silence(self, mock_fetch_url, mock_get_version):
grafana_iface = grafana_silence.GrafanaSilenceInterface(module)
silence_id = "470b7116-8f06-4bb6-9e6c-6258aa92218e"
result = grafana_iface.delete_silence(silence_id)
mock_fetch_url.assert_called_once_with(
mock_fetch_url.assert_called_with(
module,
"https://grafana.example.com/api/alertmanager/grafana/api/v2/silence/470b7116-8f06-4bb6-9e6c-6258aa92218e",
data=None,
Expand Down

0 comments on commit 9dd2987

Please sign in to comment.