diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3035cfbb..2cc40362 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,13 +1,9 @@ .github # Repo maintainers, and goverance team (like Anisble's @ansible-commit-external) -* @rrey @seuf @gundalow-collections/community-goverance-team +* @rrey @seuf # or possibly, we may want to define teams at the org level. -# * @gundalow-collections/grafana-maintainers # Example of maintainer of just a single plugin #grafana/plugins/modules/grafana_plugin.py @someone-else - - -.github/ @gundalow diff --git a/.github/workflows/ansible-test.yml b/.github/workflows/ansible-test.yml index e8f2cd6b..fff19d23 100644 --- a/.github/workflows/ansible-test.yml +++ b/.github/workflows/ansible-test.yml @@ -12,7 +12,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - python_version: ["3.9"] + python_version: ["3.10"] ansible_version: ["stable-2.13", "stable-2.14", "devel"] steps: - name: Perform testing @@ -29,7 +29,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - python_version: ["3.9"] + python_version: ["3.10"] ansible_version: ["stable-2.13", "stable-2.14", "devel"] steps: - name: Perform testing @@ -45,9 +45,9 @@ jobs: strategy: fail-fast: false matrix: - grafana_version: ["9.2.6", "8.5.15", "7.5.16"] + grafana_version: ["9.5.13", "8.5.27", "10.2.0"] ansible_version: ["stable-2.13", "stable-2.14", "devel"] - python_version: ["3.9"] + python_version: ["3.10"] services: grafana: image: grafana/grafana:${{ matrix.grafana_version }} diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9181c9b9..5385a3f3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,23 @@ Grafana Collection Release Notes .. contents:: Topics +v1.6.1 +====== + +Minor Changes +------------- + +- Bump version of Python used in tests to 3.10 +- Enable datasource option `time_interval` for prometheus +- Fix documentation url for Ansible doc website +- Now testing against Grafana 9.5.13, 8.5.27, 10.2.0 + +Bugfixes +-------- + +- Fix error with datasources configured without basicAuth +- grafana_folder, fix an issue during delete (starting Grafana 9.3) + v1.6.0 ====== diff --git a/README.md b/README.md index c2e7eb65..1d4ddb2c 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Click on the name of a plugin or module to view that content's documentation: We aim at keeping the last 3 Major versions of Grafana tested. This collection is currently testing the modules against following versions of Grafana: ``` -grafana_version: ["9.2.6", "8.5.15", "7.5.16"] +grafana_version: ["9.5.13", "8.5.27", "10.2.0"] ``` ## Installation and Usage diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 0c670bbc..0f1f2932 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -218,3 +218,19 @@ releases: name: grafana_organization_user namespace: '' release_date: '2023-02-19' + 1.6.1: + changes: + bugfixes: + - Fix error with datasources configured without basicAuth + - grafana_folder, fix an issue during delete (starting Grafana 9.3) + minor_changes: + - Bump version of Python used in tests to 3.10 + - Enable datasource option `time_interval` for prometheus + - Fix documentation url for Ansible doc website + - Now testing against Grafana 9.5.13, 8.5.27, 10.2.0 + fragments: + - 285_fix_doc.yml + - 294-bump-grafana-version.yml + - 300_datasource_prometheus_time_interval.yml + - fix-316.yml + release_date: '2023-11-05' diff --git a/changelogs/fragments/285_fix_doc.yml b/changelogs/fragments/285_fix_doc.yml new file mode 100644 index 00000000..74db5e2c --- /dev/null +++ b/changelogs/fragments/285_fix_doc.yml @@ -0,0 +1,6 @@ +--- + +minor_changes: + - Fix documentation url for Ansible doc website + +... diff --git a/changelogs/fragments/294-bump-grafana-version.yml b/changelogs/fragments/294-bump-grafana-version.yml new file mode 100644 index 00000000..7149cfe0 --- /dev/null +++ b/changelogs/fragments/294-bump-grafana-version.yml @@ -0,0 +1,5 @@ +minor_changes: + - Bump version of Python used in tests to 3.10 + - Now testing against Grafana 9.5.13, 8.5.27, 10.2.0 +bugfixes: + - grafana_folder, fix an issue during delete (starting Grafana 9.3) diff --git a/changelogs/fragments/318-org_users_by_org_name.yml b/changelogs/fragments/318-org_users_by_org_name.yml new file mode 100644 index 00000000..ef8d4959 --- /dev/null +++ b/changelogs/fragments/318-org_users_by_org_name.yml @@ -0,0 +1,7 @@ +--- + +minor_changes: + - Add parameter `org_name` to `grafana_organization_user` + +trivial: + - Add tests for new `grafana_organization_user`-parameter `org_name` diff --git a/changelogs/fragments/321-action-groups-org-users.yml b/changelogs/fragments/321-action-groups-org-users.yml new file mode 100644 index 00000000..d62f076b --- /dev/null +++ b/changelogs/fragments/321-action-groups-org-users.yml @@ -0,0 +1,4 @@ +--- + +bugfixes: + - Add `grafana_organiazion_user` to `action_groups.grafana` diff --git a/changelogs/fragments/fix-316.yml b/changelogs/fragments/fix-316.yml new file mode 100644 index 00000000..4569d445 --- /dev/null +++ b/changelogs/fragments/fix-316.yml @@ -0,0 +1,2 @@ +bugfixes: + - Fix error with datasources configured without basicAuth \ No newline at end of file diff --git a/galaxy.yml b/galaxy.yml index 8b482377..739c4c33 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,6 +1,6 @@ namespace: community name: grafana -version: 1.6.0 +version: 1.6.1 readme: README.md authors: - RĂ©mi REY (@rrey) diff --git a/hacking/requirements.txt b/hacking/requirements.txt index ced51d09..2c24336e 100644 --- a/hacking/requirements.txt +++ b/hacking/requirements.txt @@ -1 +1 @@ -requests==2.28.0 +requests==2.31.0 diff --git a/meta/runtime.yml b/meta/runtime.yml index 8d9b13a4..ed9ac426 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -2,11 +2,12 @@ requires_ansible: '>=2.9.0' action_groups: grafana: - - grafana_dashboard - - grafana_datasource - - grafana_folder - - grafana_notification_channel - - grafana_organization - - grafana_plugin - - grafana_team - - grafana_user + - grafana_dashboard + - grafana_datasource + - grafana_folder + - grafana_notification_channel + - grafana_organization + - grafana_organization_user + - grafana_plugin + - grafana_team + - grafana_user diff --git a/plugins/modules/grafana_dashboard.py b/plugins/modules/grafana_dashboard.py index 99801d49..cc2654f6 100644 --- a/plugins/modules/grafana_dashboard.py +++ b/plugins/modules/grafana_dashboard.py @@ -101,7 +101,7 @@ grafana_api_key: "{{ grafana_api_key }}" folder: zabbix dashboard_id: 6098 - dashbord_revision: 1 + dashboard_revision: 1 - name: Import Grafana dashboard zabbix community.grafana.grafana_dashboard: diff --git a/plugins/modules/grafana_datasource.py b/plugins/modules/grafana_datasource.py index 20658dd1..d9bc7f2f 100644 --- a/plugins/modules/grafana_datasource.py +++ b/plugins/modules/grafana_datasource.py @@ -68,15 +68,18 @@ (index name), C(mysql) or C(postgres). required: false type: str + default: '' user: description: - The datasource login user for influxdb datasources. type: str + default: '' password: description: - The datasource password. - Stored as secure data, see C(enforce_secure_data) and notes! type: str + default: '' basic_auth_user: description: - The datasource basic auth user. @@ -181,6 +184,7 @@ - Monthly - Yearly type: str + default: '' tsdb_version: description: - The opentsdb version. @@ -281,7 +285,6 @@ aws_custom_metrics_namespaces: description: - Namespaces of Custom Metrics for CloudWatch datasource type - default: '' required: false type: str azure_cloud: @@ -511,7 +514,7 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.six.moves.urllib.parse import quote -from ansible.module_utils.urls import fetch_url, url_argument_spec, basic_auth_header +from ansible.module_utils.urls import fetch_url, basic_auth_header from ansible_collections.community.grafana.plugins.module_utils import base @@ -533,7 +536,8 @@ def compare_datasources(new, current, compareSecureData=True): if 'readOnly' in current: del current['readOnly'] if current['basicAuth'] is False: - del current['basicAuthUser'] + if 'basicAuthUser' in current: + del current['basicAuthUser'] if 'password' in current: del current['password'] if 'basicAuthPassword' in current: diff --git a/plugins/modules/grafana_folder.py b/plugins/modules/grafana_folder.py index d39e56e4..380a510c 100644 --- a/plugins/modules/grafana_folder.py +++ b/plugins/modules/grafana_folder.py @@ -215,7 +215,10 @@ def _send_request(self, url, data=None, headers=None, method="GET"): error_msg = resp.read()['message'] self._module.fail_json(failed=True, msg=error_msg) elif status_code == 200: - return self._module.from_json(resp.read()) + # XXX: Grafana folders endpoint stopped sending back json in response for delete operations + # see https://github.com/grafana/grafana/issues/77673 + response = resp.read() or "{}" + return self._module.from_json(response) self._module.fail_json(failed=True, msg="Grafana Folders API answered with HTTP %d" % status_code) def get_version(self): diff --git a/plugins/modules/grafana_organization_user.py b/plugins/modules/grafana_organization_user.py index 72496b84..14195fdb 100644 --- a/plugins/modules/grafana_organization_user.py +++ b/plugins/modules/grafana_organization_user.py @@ -57,6 +57,12 @@ default: 1 description: - Organization ID. + - Mutually exclusive with `org_name`. + org_name: + type: str + description: + - Organization name. + - Mutually exclusive with `org_id`. extends_documentation_fragment: - community.grafana.basic_auth @@ -156,6 +162,12 @@ def _api_call(self, method, path, payload): data = json.dumps(payload) return fetch_url(self._module, self.grafana_url + '/api/' + path, headers=self.headers, method=method, data=data) + def _organization_by_name(self, org_name): + r, info = self._api_call('GET', 'orgs/name/%s' % org_name, None) + if info['status'] != 200: + raise GrafanaAPIException("Unable to retrieve organization: %s" % info) + return json.loads(to_text(r.read())) + def _organization_users(self, org_id): r, info = self._api_call('GET', 'orgs/%d/users' % org_id, None) if info['status'] != 200: @@ -178,7 +190,7 @@ def _remove_organization_user(self, org_id, user_id): def _organization_user_by_login(self, org_id, login): for user in self._organization_users(org_id): - if user['name'] == login or user['email'] == login: + if login in (user['login'], user['email']): return user def create_or_update_user(self, org_id, login, role): @@ -232,12 +244,16 @@ def main(): argument_spec.pop('grafana_api_key') argument_spec.update( org_id=dict(type='int', default=1), + org_name=dict(type='str'), login=dict(type='str', required=True), role=dict(type='str', choices=['viewer', 'editor', 'admin'], default='viewer'), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=False, + mutually_exclusive=[ + ('org_id', 'org_name'), + ], required_if=[ ['state', 'present', ['role']], ] @@ -246,6 +262,10 @@ def main(): org_id = module.params['org_id'] login = module.params['login'] iface = GrafanaOrganizationUserInterface(module) + if module.params['org_name']: + org_name = module.params['org_name'] + organization = iface._organization_by_name(org_name) + org_id = organization['id'] if module.params['state'] == 'present': role = module.params['role'].capitalize() result = iface.create_or_update_user(org_id, login, role) diff --git a/tests/integration/targets/grafana_dashboard/tasks/main.yml b/tests/integration/targets/grafana_dashboard/tasks/main.yml index 4d9138fc..570fb4d8 100644 --- a/tests/integration/targets/grafana_dashboard/tasks/main.yml +++ b/tests/integration/targets/grafana_dashboard/tasks/main.yml @@ -1,7 +1,7 @@ - block: - - include: dashboard-from-url.yml - - include: delete-dashboard.yml - - include: dashboard-from-id.yml - - include: dashboard-from-file.yml - - include: dashboard-export.yml - - include: dashboard-folder-destination.yml + - include_tasks: dashboard-from-url.yml + - include_tasks: delete-dashboard.yml + - include_tasks: dashboard-from-id.yml + - include_tasks: dashboard-from-file.yml + - include_tasks: dashboard-export.yml + - include_tasks: dashboard-folder-destination.yml diff --git a/tests/integration/targets/grafana_datasource/tasks/main.yml b/tests/integration/targets/grafana_datasource/tasks/main.yml index b5798e9a..312ad347 100644 --- a/tests/integration/targets/grafana_datasource/tasks/main.yml +++ b/tests/integration/targets/grafana_datasource/tasks/main.yml @@ -1,16 +1,16 @@ --- - block: - - include: errors.yml - - include: elastic.yml - - include: influx.yml - - include: postgres.yml - - include: cloudwatch.yml - - include: thruk.yml - - include: loki.yml - - include: zabbix.yml - - include: redis.yml - - include: azure.yml - - include: uid.yml + - include_tasks: errors.yml + - include_tasks: elastic.yml + - include_tasks: influx.yml + - include_tasks: postgres.yml + - include_tasks: cloudwatch.yml + - include_tasks: thruk.yml + - include_tasks: loki.yml + - include_tasks: zabbix.yml + - include_tasks: redis.yml + - include_tasks: azure.yml + - include_tasks: uid.yml ... diff --git a/tests/integration/targets/grafana_notification_channel/tasks/main.yml b/tests/integration/targets/grafana_notification_channel/tasks/main.yml index ada6338c..ceaf72ad 100644 --- a/tests/integration/targets/grafana_notification_channel/tasks/main.yml +++ b/tests/integration/targets/grafana_notification_channel/tasks/main.yml @@ -1,20 +1,20 @@ --- - block: - - include: dingding.yml - - include: discord.yml - - include: email.yml - - include: googlechat.yml - - include: hipchat.yml - - include: kafka.yml -# - include: line.yml - - include: teams.yml - - include: opsgenie.yml - - include: pagerduty.yml - - include: prometheus.yml - - include: pushover.yml - - include: sensu.yml - - include: slack-and-beyond.yml - - include: telegram.yml -# - include: threema.yml - - include: victorops.yml - - include: webhook.yml + - include_tasks: dingding.yml + - include_tasks: discord.yml + - include_tasks: email.yml + - include_tasks: googlechat.yml + - include_tasks: hipchat.yml + - include_tasks: kafka.yml +# - include_tasks: line.yml + - include_tasks: teams.yml + - include_tasks: opsgenie.yml + - include_tasks: pagerduty.yml + - include_tasks: prometheus.yml + - include_tasks: pushover.yml + - include_tasks: sensu.yml + - include_tasks: slack-and-beyond.yml + - include_tasks: telegram.yml +# - include_tasks: threema.yml + - include_tasks: victorops.yml + - include_tasks: webhook.yml diff --git a/tests/integration/targets/grafana_organization_user/tasks/main.yml b/tests/integration/targets/grafana_organization_user/tasks/main.yml index 6ac1f131..3ede4f7c 100644 --- a/tests/integration/targets/grafana_organization_user/tasks/main.yml +++ b/tests/integration/targets/grafana_organization_user/tasks/main.yml @@ -92,7 +92,7 @@ state: present register: org -- name: Add user to the organization +- name: Add user to the new organization by org_id community.grafana.grafana_organization_user: url: "{{ grafana_url }}" url_username: "{{ grafana_username }}" @@ -108,3 +108,48 @@ - "result.changed == true" - "result.user.orgId == org.org.id" - "result.user.role == 'Admin'" + +- name: Remove user from new organization by org_id + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_id: "{{ org.org.id }}" + login: orgtest + state: absent + register: result +- assert: + that: + - "result.failed == false" + - "result.changed == true" + +- name: Add user to the new organization by org_name + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_name: "{{ org.org.name }}" + login: orgtest + role: admin + state: present + register: result +- assert: + that: + - "result.failed == false" + - "result.changed == true" + - "result.user.orgId == org.org.id" + - "result.user.role == 'Admin'" + +- name: Remove user from new organization by org_name + community.grafana.grafana_organization_user: + url: "{{ grafana_url }}" + url_username: "{{ grafana_username }}" + url_password: "{{ grafana_password }}" + org_name: "{{ org.org.name }}" + login: orgtest + state: absent + register: result +- assert: + that: + - "result.failed == false" + - "result.changed == true" diff --git a/tests/sanity/ignore-2.16.txt b/tests/sanity/ignore-2.16.txt new file mode 100644 index 00000000..5c82494f --- /dev/null +++ b/tests/sanity/ignore-2.16.txt @@ -0,0 +1,4 @@ +plugins/modules/grafana_dashboard.py validate-modules:invalid-argument-name +tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py pep8:W291 +hacking/check_fragment.sh shebang +hacking/find_grafana_versions.py shebang diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt new file mode 100644 index 00000000..5c82494f --- /dev/null +++ b/tests/sanity/ignore-2.17.txt @@ -0,0 +1,4 @@ +plugins/modules/grafana_dashboard.py validate-modules:invalid-argument-name +tests/unit/modules/grafana/grafana_plugin/test_grafana_plugin.py pep8:W291 +hacking/check_fragment.sh shebang +hacking/find_grafana_versions.py shebang diff --git a/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py b/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py index d2fba0fe..0a68fda7 100644 --- a/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py +++ b/tests/unit/modules/grafana/grafana_datasource/test_grafana_datasource.py @@ -1,7 +1,7 @@ from __future__ import (absolute_import, division, print_function) from unittest import TestCase -from unittest.mock import call, patch, MagicMock +from unittest.mock import patch from ansible_collections.community.grafana.plugins.modules import grafana_datasource from ansible.module_utils._text import to_bytes from ansible.module_utils import basic diff --git a/tests/unit/modules/grafana/grafana_team/test_grafana_team.py b/tests/unit/modules/grafana/grafana_team/test_grafana_team.py index c59953af..491b863d 100644 --- a/tests/unit/modules/grafana/grafana_team/test_grafana_team.py +++ b/tests/unit/modules/grafana/grafana_team/test_grafana_team.py @@ -1,7 +1,7 @@ from __future__ import (absolute_import, division, print_function) from unittest import TestCase -from unittest.mock import patch, MagicMock +from unittest.mock import patch from ansible_collections.community.grafana.plugins.modules import grafana_team from ansible.module_utils._text import to_bytes from ansible.module_utils import basic diff --git a/tests/unit/modules/grafana/grafana_user/test_grafana_user.py b/tests/unit/modules/grafana/grafana_user/test_grafana_user.py index 925c0165..80c51409 100644 --- a/tests/unit/modules/grafana/grafana_user/test_grafana_user.py +++ b/tests/unit/modules/grafana/grafana_user/test_grafana_user.py @@ -1,7 +1,7 @@ from __future__ import (absolute_import, division, print_function) from unittest import TestCase -from unittest.mock import call, patch, MagicMock +from unittest.mock import call, patch from ansible_collections.community.grafana.plugins.modules import grafana_user from ansible.module_utils._text import to_bytes from ansible.module_utils import basic