From d183af929b6601c34b4d764929010c01ed07f318 Mon Sep 17 00:00:00 2001 From: Adrien Waksberg Date: Thu, 7 Sep 2023 10:36:42 +0200 Subject: [PATCH] feat: upgrade to influxdb 2 --- CHANGELOG.md | 4 + README.md | 100 +++++------ defaults/main.yml | 17 +- handlers/main.yml | 5 - library/influxdb_auth.py | 235 +++++++++++++++++++++++++ library/influxdb_bucket.py | 162 +++++++++++++++++ library/influxdb_org.py | 112 ++++++++++++ library/influxdb_setup.py | 69 ++++++++ library/influxdb_user.py | 124 +++++++++++++ meta/main.yml | 7 +- molecule/default/converge.yml | 59 +++---- molecule/default/molecule.yml | 15 +- molecule/default/tests/test_default.py | 70 ++++---- tasks/config.yml | 69 -------- tasks/install.yml | 42 ----- tasks/main.yml | 104 ++++++++++- 16 files changed, 933 insertions(+), 261 deletions(-) delete mode 100644 handlers/main.yml create mode 100644 library/influxdb_auth.py create mode 100644 library/influxdb_bucket.py create mode 100644 library/influxdb_org.py create mode 100644 library/influxdb_setup.py create mode 100644 library/influxdb_user.py delete mode 100644 tasks/config.yml delete mode 100644 tasks/install.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 121595f..c9ae60b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ Which is based on [Keep A Changelog](http://keepachangelog.com/) ## [Unreleased] +### Break + +- upgrade to influxdb 2 + ## [2.0.0] 2021-08-18 ### Break diff --git a/README.md b/README.md index fde82f1..82a30f8 100644 --- a/README.md +++ b/README.md @@ -3,51 +3,64 @@ [![Version](https://img.shields.io/badge/latest_version-2.0.0-green.svg)](https://git.yaegashi.fr/nishiki/ansible-role-influxdb/releases) [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://git.yaegashi.fr/nishiki/ansible-role-influxdb/src/branch/master/LICENSE) -Install and configure InfluxDB +Install and configure InfluxDB 2 ## Requirements -* Ansible >= 2.9 +* Ansible >= 2.10 * Debian - * Buster - * Bullseye + * Bookworm ## Role variables -* `influxdb_databases` - array with the databases name -* `influxdb_users` - array with the users informations +- `influxdb_init_username` - user created during the influxdb setup +- `influxdb_init_org` - organization created during the influxdb setup +- `influxdb_init_bucket` - bucket created during the influxdb setup +- `influxdb_api_token` - token to manage influxdb +- `influxdb_orgs` - hash with organizations -``` -- name: test - password: secret - admin: true - grants: - - database: collectd - privilege: WRITE - state: present -``` + ```yaml + myorg: + description: it's a test + state: present + ``` -- `influxdb_retention_policies` - array with the retention policies +- `influxdb_buckets` - hash with the buckets -``` - - name: default - database: test_db - duration: 1d - default: true -``` + ```yaml + mybucket: + description: KFC + retention: 3600 + org: neworg + state: present + ``` -* `influxdb_api_user` - set the api user if you have enabled http authentification (default: `admin`) -* `influxdb_api_password` - set the api password if you have enabled http authentification -* `influxdb_api_port` - set the api port (default: `8086`) -* `influxdb_config` - hash with the influxdb configuration (see [influxdb documentation](https://docs.influxdata.com/influxdb/v1.7/administration/config/)) +- `influxdb_users` - hash with the users -``` - meta: - dir: /var/lib/influxdb/meta - data: - dir: /var/lib/influxdb/data - wal-dir: /var/lib/influxdb/wal -``` + ```yaml + myuser: + status: active + state: present + ``` + +- `influxdb_authorizations` - array with the authorizations + + ```yaml + - user: myuser + description: write bucket + org: neworg + status: active + state: present + permissions: + - action: write + resource: + type: buckets + org: myorg + name: mybucket + - action: read + resource: + type: buckets + ``` ## How to use @@ -59,25 +72,12 @@ Install and configure InfluxDB ## Development -### Test syntax with yamllint - -* install `python` and `python-pip` -* install yamllint `pip install yamllint` -* run `yamllint .` - -### Test syntax with ansible-lint - -* install `python` and `python-pip` -* install yamllint `pip install ansible-lint` -* run `ansible-lint .` - -### Tests with docker +### Test with molecule and docker * install [docker](https://docs.docker.com/engine/installation/) -* install ruby -* install bundler `gem install bundler` -* install dependencies `bundle install` -* run the tests `kitchen test` +* install `python3` and `python3-pip` +* install molecule and dependencies `pip3 install molecule molecule-plugins[docker] ansible-lint pytest-testinfra yamllint` +* run `molecule test` ## License diff --git a/defaults/main.yml b/defaults/main.yml index 1de8790..9732f9c 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,14 +1,5 @@ --- -influxdb_api_user: admin -influxdb_api_port: 8086 -influxdb_default_config: - meta: - dir: /var/lib/influxdb/meta - data: - dir: /var/lib/influxdb/data - wal-dir: /var/lib/influxdb/wal -influxdb_config: {} -influxdb_full_config: '{{ influxdb_default_config|combine(influxdb_config) }}' -influxdb_databases: [] -influxdb_policies: [] -influxdb_users: [] +influxdb_orgs: {} +influxdb_buckets: {} +influxdb_users: {} +influxdb_authorizations: [] diff --git a/handlers/main.yml b/handlers/main.yml deleted file mode 100644 index 547bd40..0000000 --- a/handlers/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: restart influxdb - ansible.builtin.service: - name: influxdb - state: restarted diff --git a/library/influxdb_auth.py b/library/influxdb_auth.py new file mode 100644 index 0000000..d78c4bf --- /dev/null +++ b/library/influxdb_auth.py @@ -0,0 +1,235 @@ +#!/usr/bin/python3 + +from ansible.module_utils.basic import AnsibleModule +import requests +import json + + +class InfluxdbAuth: + def __init__(self, api_url, api_token, org, user, permissions): + self.api_url = api_url + self.headers = {'Authorization': 'Token {}'.format(api_token)} + self.org = org + self.user = user + self.permissions = permissions + + def exists(self): + url = '{}/api/v2/authorizations'.format(self.api_url) + r = requests.get( + url, + headers=self.headers, + params={'org': self.org, 'user': self.user} + ) + + if r.status_code == 404: + return False + elif r.status_code == 200: + data = json.loads(r.text) + for auth in data['authorizations']: + if self.check_permissions(auth['permissions']): + self.id = auth['id'] + self.status = auth['status'] + self.description = auth['description'] + return True + return False + + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def check_permissions(self, permissions): + for pa in self.permissions: + find = False + for pb in permissions: + if pa['action'] != pb['action']: + continue + if pa['resource']['type'] != pb['resource']['type']: + continue + if 'org' in pa['resource'] and 'org' in pb['resource']: + if pa['resource']['org'] != pb['resource']['org']: + continue + if 'name' in pa['resource'] and 'name' in pb['resource']: + if pa['resource']['name'] != pb['resource']['name']: + continue + find = True + break + + if not find: + return False + return True + + def transform_permissions(self): + permissions = list() + for perm in self.permissions: + org = None + if 'org' in perm['resource'] and 'orgID' not in perm['resource']: + org = perm['resource']['org'] + perm['resource']['orgID'] = self.get_orgid(org) + if 'name' in perm['resource'] and 'id' not in perm['resource']: + if perm['resource']['type'] == 'buckets': + if org is None: + org = self.org + perm['resource']['id'] = self.get_bucketid( + perm['resource']['name'], org) + permissions.append(perm) + + return permissions + + def has_changed(self, status, description): + if self.status != status: + return True + if self.description != description: + return True + return False + + def create(self, status, description): + url = '{}/api/v2/authorizations'.format(self.api_url) + r = requests.post( + url, + headers=self.headers, + json={ + 'userID': self.get_userid(self.user, self.org), + 'orgID': self.get_orgid(self.org), + 'status': status, + 'description': description, + 'permissions': self.transform_permissions() + } + ) + + if r.status_code != 201: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def delete(self): + url = '{}/api/v2/authorizations/{}'.format(self.api_url, self.id) + r = requests.delete(url, headers=self.headers) + + if r.status_code != 204: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def update(self, status, description): + url = '{}/api/v2/authorizations/{}'.format(self.api_url, self.id) + r = requests.patch( + url, + headers=self.headers, + json={ + 'description': description, + 'status': status + } + ) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def get_bucketid(self, bucket_name, org): + url = '{}/api/v2/buckets'.format(self.api_url) + r = requests.get( + url, + headers=self.headers, + params={'name': bucket_name, 'org': org} + ) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + data = json.loads(r.text) + for bucket in data['buckets']: + if bucket['name'] == bucket_name: + return bucket['id'] + + raise Exception('Influxdb', 'Don\'t get the bucketID') + + def get_orgid(self, org_name): + url = '{}/api/v2/orgs'.format(self.api_url) + r = requests.get(url, headers=self.headers, params={'org': org_name}) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + data = json.loads(r.text) + for org in data['orgs']: + if org['name'] == org_name: + return org['id'] + + raise Exception('Influxdb', 'Don\'t get the orgID') + + def get_userid(self, user_name, org): + url = '{}/api/v2/users'.format(self.api_url) + r = requests.get( + url, + headers=self.headers, + params={'name': user_name, 'org': org} + ) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + data = json.loads(r.text) + for user in data['users']: + if user['name'] == user_name: + return user['id'] + + raise Exception('Influxdb', 'Don\'t get the userID') + + +def main(): + fields = { + 'user': {'type': 'str', 'required': True}, + 'org': {'type': 'str', 'required': True}, + 'permissions': {'type': 'list', 'required': True}, + 'status': {'type': 'str', + 'default': 'active', + 'choice': ['active', 'inactive']}, + 'description': {'type': 'str', 'default': ''}, + 'api_url': {'type': 'str', 'default': 'http://127.0.0.1:8086'}, + 'api_token': {'type': 'str', 'required': True}, + 'state': {'type': 'str', + 'default': 'present', + 'choice': ['present', 'absent']}, + } + module = AnsibleModule(argument_spec=fields) + + auth = InfluxdbAuth( + module.params['api_url'], + module.params['api_token'], + module.params['org'], + module.params['user'], + module.params['permissions'], + ) + changed = False + + if auth.exists(): + if module.params['state'] == 'absent': + auth.delete() + changed = True + elif auth.has_changed(module.params['status'], + module.params['description']): + auth.update(module.params['status'], module.params['description']) + changed = True + elif module.params['state'] == 'present': + auth.create(module.params['status'], module.params['description']) + changed = True + + module.exit_json(changed=changed) + + +if __name__ == '__main__': + main() diff --git a/library/influxdb_bucket.py b/library/influxdb_bucket.py new file mode 100644 index 0000000..50e220c --- /dev/null +++ b/library/influxdb_bucket.py @@ -0,0 +1,162 @@ +#!/usr/bin/python3 + +from ansible.module_utils.basic import AnsibleModule +import requests +import json + + +class InfluxdbBucket: + def __init__(self, api_url, api_token, name, org): + self.api_url = api_url + self.headers = {'Authorization': 'Token {}'.format(api_token)} + self.name = name + self.org = org + + def exists(self): + url = '{}/api/v2/buckets'.format(self.api_url) + r = requests.get( + url, + headers=self.headers, + params={'name': self.name, 'org': self.org} + ) + + if r.status_code == 404: + return False + elif r.status_code == 200: + data = json.loads(r.text) + for bucket in data['buckets']: + if bucket['name'] == self.name: + self.id = bucket['id'] + self.retention = bucket['retentionRules'][0]['everySeconds'] + if 'description' in bucket: + self.description = bucket['description'] + return True + return False + + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def has_changed(self, description, retention): + if self.description != description: + return True + if self.retention != retention: + return True + return False + + def get_orgid(self): + url = '{}/api/v2/orgs'.format(self.api_url) + r = requests.get(url, headers=self.headers, params={'org': self.org}) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + data = json.loads(r.text) + for org in data['orgs']: + if org['name'] == self.org: + return org['id'] + + raise Exception('Influxdb', 'Don\'t get the orgID') + + def create(self, description, retention): + url = '{}/api/v2/buckets'.format(self.api_url) + r = requests.post( + url, + headers=self.headers, + json={ + 'name': self.name, + 'description': description, + 'orgID': self.get_orgid(), + 'retentionRules': [{ + 'everySeconds': retention, + 'shardGroupDurationSeconds': 0, + 'type': 'expire' + }] + } + ) + + if r.status_code != 201: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def delete(self): + url = '{}/api/v2/buckets/{}'.format(self.api_url, self.id) + r = requests.delete(url, headers=self.headers) + + if r.status_code != 204: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def update(self, description, retention): + url = '{}/api/v2/buckets/{}'.format(self.api_url, self.id) + r = requests.patch( + url, + headers=self.headers, + json={ + 'name': self.name, + 'description': description, + 'retentionRules': [{ + 'everySeconds': retention, + 'shardGroupDurationSeconds': 0, + 'type': 'expire' + }] + } + ) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + +def main(): + fields = { + 'name': {'type': 'str', 'required': True}, + 'org': {'type': 'str', 'required': True}, + 'description': {'type': 'str', 'default': ''}, + 'retention': {'type': 'int', 'default': 0}, + 'api_url': {'type': 'str', 'default': 'http://127.0.0.1:8086'}, + 'api_token': {'type': 'str', 'required': True}, + 'state': {'type': 'str', + 'default': 'present', + 'choice': ['present', 'absent']}, + } + module = AnsibleModule(argument_spec=fields) + + bucket = InfluxdbBucket( + module.params['api_url'], + module.params['api_token'], + module.params['name'], + module.params['org'], + ) + changed = False + + if bucket.exists(): + if module.params['state'] == 'absent': + bucket.delete() + changed = True + elif bucket.has_changed(module.params['description'], + module.params['retention']): + bucket.update( + module.params['description'], + module.params['retention'] + ) + changed = True + elif module.params['state'] == 'present': + bucket.create(module.params['description'], module.params['retention']) + changed = True + + module.exit_json(changed=changed) + + +if __name__ == '__main__': + main() diff --git a/library/influxdb_org.py b/library/influxdb_org.py new file mode 100644 index 0000000..b86cda4 --- /dev/null +++ b/library/influxdb_org.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 + +from ansible.module_utils.basic import AnsibleModule +import requests +import json + + +class InfluxdbOrg: + def __init__(self, api_url, api_token, name): + self.api_url = api_url + self.headers = {'Authorization': 'Token {}'.format(api_token)} + self.name = name + + def exists(self): + url = '{}/api/v2/orgs'.format(self.api_url) + r = requests.get(url, headers=self.headers, params={'org': self.name}) + + if r.status_code == 404: + return False + elif r.status_code == 200: + data = json.loads(r.text) + for org in data['orgs']: + if org['name'] == self.name: + self.id = org['id'] + self.description = org['description'] + return True + return False + + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def has_changed(self, description): + if self.description == description: + return False + return True + + def create(self, description): + url = '{}/api/v2/orgs'.format(self.api_url) + r = requests.post( + url, + headers=self.headers, + json={'name': self.name, 'description': description} + ) + + if r.status_code != 201: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def delete(self): + url = '{}/api/v2/orgs/{}'.format(self.api_url, self.id) + r = requests.delete(url, headers=self.headers) + + if r.status_code != 204: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def update(self, description): + url = '{}/api/v2/orgs/{}'.format(self.api_url, self.id) + r = requests.patch( + url, + headers=self.headers, + json={'name': self.name, 'description': description} + ) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + +def main(): + fields = { + 'name': {'type': 'str', 'required': True}, + 'description': {'type': 'str', 'default': ''}, + 'api_url': {'type': 'str', 'default': 'http://127.0.0.1:8086'}, + 'api_token': {'type': 'str', 'required': True}, + 'state': {'type': 'str', + 'default': 'present', + 'choice': ['present', 'absent']}, + } + module = AnsibleModule(argument_spec=fields) + + org = InfluxdbOrg( + module.params['api_url'], + module.params['api_token'], + module.params['name'], + ) + changed = False + + if org.exists(): + if module.params['state'] == 'absent': + org.delete() + changed = True + elif org.has_changed(module.params['description']): + org.update(module.params['description']) + changed = True + elif module.params['state'] == 'present': + org.create(module.params['description']) + changed = True + + module.exit_json(changed=changed) + + +if __name__ == '__main__': + main() diff --git a/library/influxdb_setup.py b/library/influxdb_setup.py new file mode 100644 index 0000000..47ab03e --- /dev/null +++ b/library/influxdb_setup.py @@ -0,0 +1,69 @@ +#!/usr/bin/python3 + +from ansible.module_utils.basic import AnsibleModule +import requests +import json + + +class InfluxdbSetup: + def __init__(self, api_url): + self.api_url = api_url + + def already(self): + url = '{}/api/v2/setup'.format(self.api_url) + r = requests.get(url) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + data = json.loads(r.text) + if 'allowed' not in data or data['allowed'] is True: + return False + return True + + def run(self, username, org, bucket, token): + url = '{}/api/v2/setup'.format(self.api_url) + r = requests.post(url, json={ + 'username': username, + 'org': org, + 'bucket': bucket, + 'token': token, + }) + + if r.status_code != 201: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + +def main(): + fields = { + 'username': {'type': 'str', 'required': True}, + 'org': {'type': 'str', 'required': True}, + 'bucket': {'type': 'str', 'required': True}, + 'token': {'type': 'str', 'required': True}, + 'api_url': {'type': 'str', 'default': 'http://127.0.0.1:8086'}, + } + module = AnsibleModule(argument_spec=fields) + + setup = InfluxdbSetup( + module.params['api_url'], + ) + + if setup.already() is True: + module.exit_json(changed=False) + setup.run( + module.params['username'], + module.params['org'], + module.params['bucket'], + module.params['token'], + ) + module.exit_json(changed=True) + + +if __name__ == '__main__': + main() diff --git a/library/influxdb_user.py b/library/influxdb_user.py new file mode 100644 index 0000000..ebbc0b8 --- /dev/null +++ b/library/influxdb_user.py @@ -0,0 +1,124 @@ +#!/usr/bin/python3 + +from ansible.module_utils.basic import AnsibleModule +import requests +import json + + +class InfluxdbUser: + def __init__(self, api_url, api_token, name): + self.api_url = api_url + self.headers = {'Authorization': 'Token {}'.format(api_token)} + self.name = name + + def exists(self): + url = '{}/api/v2/users'.format(self.api_url) + r = requests.get( + url, + headers=self.headers, + params={'name': self.name} + ) + + if r.status_code == 404: + return False + elif r.status_code == 200: + data = json.loads(r.text) + for user in data['users']: + if user['name'] == self.name: + self.id = user['id'] + self.status = user['status'] + return True + return False + + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def has_changed(self, status): + if self.status != status: + return True + return False + + def create(self, status): + url = '{}/api/v2/users'.format(self.api_url) + r = requests.post( + url, + headers=self.headers, + json={ + 'name': self.name, + 'status': status + } + ) + + if r.status_code != 201: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def delete(self): + url = '{}/api/v2/users/{}'.format(self.api_url, self.id) + r = requests.delete(url, headers=self.headers) + + if r.status_code != 204: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + def update(self, status): + url = '{}/api/v2/users/{}'.format(self.api_url, self.id) + r = requests.patch( + url, + headers=self.headers, + json={ + 'name': self.name, + 'status': status + } + ) + + if r.status_code != 200: + raise Exception( + 'Influxdb', + 'Bad status code {}: {}'.format(r.status_code, r.text) + ) + + +def main(): + fields = { + 'name': {'type': 'str', 'required': True}, + 'status': {'type': 'str', + 'default': 'active', + 'choice': ['active', 'inactive']}, + 'api_url': {'type': 'str', 'default': 'http://127.0.0.1:8086'}, + 'api_token': {'type': 'str', 'required': True}, + 'state': {'type': 'str', + 'default': 'present', + 'choice': ['present', 'absent']}, + } + module = AnsibleModule(argument_spec=fields) + + user = InfluxdbUser( + module.params['api_url'], + module.params['api_token'], + module.params['name'], + ) + changed = False + + if user.exists(): + if module.params['state'] == 'absent': + user.delete() + changed = True + elif user.has_changed(module.params['status']): + user.update(module.params['status']) + changed = True + elif module.params['state'] == 'present': + user.create(module.params['status']) + changed = True + + module.exit_json(changed=changed) + + +if __name__ == '__main__': + main() diff --git a/meta/main.yml b/meta/main.yml index b3d448a..5e5689c 100644 --- a/meta/main.yml +++ b/meta/main.yml @@ -4,15 +4,14 @@ galaxy_info: namespace: nishiki author: Adrien Waksberg company: Adrien Waksberg - description: Install and configure InfluxDB + description: Install and configure InfluxDB 2 license: Apache2 - min_ansible_version: 2.9 + min_ansible_version: "2.10" platforms: - name: Debian versions: - - buster - - bullseye + - bookworm galaxy_tags: - database diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml index 39fe29f..911c1e7 100644 --- a/molecule/default/converge.yml +++ b/molecule/default/converge.yml @@ -4,38 +4,37 @@ roles: - ansible-role-influxdb vars: - influxdb_api_password: password - influxdb_users: - - name: '{{ influxdb_api_user }}' - password: '{{ influxdb_api_password }}' - admin: yes - - name: test - password: test2 - grants: - - database: test_db - privilege: WRITE - - name: user_absent + influxdb_init_username: admin + influxdb_init_org: init_org + influxdb_init_bucket: test + influxdb_api_token: SuP3rS3cr3t + influxdb_orgs: + neworg: + description: it's a test + init_org: state: absent - influxdb_databases: - - test_db - influxdb_retention_policies: - - name: default - database: test_db - duration: 1d - default: true - influxdb_config: - '[collectd]': - enabled: true - port: 25826 - database: collectd - typesdb: /usr/share/collectd/types.db + influxdb_buckets: + mybucket: + description: KFC + retention: 3600 + org: neworg + influxdb_users: + telegraf: + user_inactive: + status: inactive + influxdb_authorizations: + - user: telegraf + description: write telegraf + org: neworg + status: active + permissions: + - action: write + resource: + type: buckets + org: neworg + name: mybucket pre_tasks: - - name: update apt cache + - name: Update apt cache ansible.builtin.apt: update_cache: true - - - name: install collectd package - ansible.builtin.package: - name: - - collectd-core diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 0507c53..89bf10a 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -2,19 +2,12 @@ driver: name: docker platforms: - - name: debian10 - image: nishiki/debian10:molecule + - name: debian12 + image: nishiki/debian12:molecule privileged: true volumes: - - /sys/fs/cgroup:/sys/fs/cgroup:ro - command: /bin/systemd - capabilities: - - SYS_ADMIN - - name: debian11 - image: nishiki/debian11:molecule - privileged: true - volumes: - - /sys/fs/cgroup:/sys/fs/cgroup:ro + - /sys/fs/cgroup:/sys/fs/cgroup:rw + cgroupns_mode: host command: /bin/systemd capabilities: - SYS_ADMIN diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py index 76b6fe0..08d0bbe 100644 --- a/molecule/default/tests/test_default.py +++ b/molecule/default/tests/test_default.py @@ -1,48 +1,48 @@ import testinfra.utils.ansible_runner -import re + +CURL = ('curl --header "Authorization: Token SuP3rS3cr3t"' + ' http://localhost:8086/api/v2') + def test_packages(host): - package = host.package('influxdb') - assert package.is_installed + package = host.package('influxdb2') + assert package.is_installed -def test_config_file(host): - path = host.file('/etc/influxdb/influxdb.conf') - assert path.exists - assert path.is_file - assert path.user == 'root' - assert path.group == 'root' - assert path.mode == 0o644 - assert path.contains('enabled = true') def test_service(host): - service = host.service('influxdb') - assert service.is_running - assert service.is_enabled + service = host.service('influxdb') + assert service.is_running + assert service.is_enabled + def test_sockets(host): - socket = host.socket('tcp://0.0.0.0:8086') - assert socket.is_listening - socket = host.socket('udp://0.0.0.0:25826') - assert socket.is_listening + socket = host.socket('tcp://0.0.0.0:8086') + assert socket.is_listening + def test_users(host): - cmd = host.run('influx -execute "SHOW USERS"') - assert cmd.succeeded - assert re.search(r'admin\s+true', cmd.stdout) - assert re.search(r'test\s+false', cmd.stdout) - assert not re.search('user_absent', cmd.stdout) + cmd = host.run('{}/users -X GET'.format(CURL)) + assert cmd.succeeded + assert '"name": "admin"' in cmd.stdout + assert '"name": "telegraf"' in cmd.stdout -def test_databases(host): - cmd = host.run('influx -execute "SHOW DATABASES"') - assert cmd.succeeded - assert 'test_db' in cmd.stdout -def test_databases(host): - cmd = host.run('influx -execute "SHOW RETENTION POLICIES ON test_db"') - assert cmd.succeeded - assert re.search(r'default\s+24h0m0s\s+1h0m0s\s+1\s+true', cmd.stdout) +def test_org(host): + cmd = host.run('{}/orgs -X GET'.format(CURL)) + assert cmd.succeeded + assert '"name": "neworg"' in cmd.stdout + assert '"name": "init_org"' not in cmd.stdout -def test_grants(host): - cmd = host.run('influx -execute "SHOW GRANTS FOR test"') - assert cmd.succeeded - assert re.search(r'test_db\s+WRITE', cmd.stdout) + +def test_bucket(host): + cmd = host.run('{}/buckets -X GET'.format(CURL)) + assert cmd.succeeded + assert '"name": "mybucket"' in cmd.stdout + assert '"everySeconds": 3600' in cmd.stdout + assert '"name": "test"' not in cmd.stdout + + +def test_auth(host): + cmd = host.run('{}/authorizations -X GET'.format(CURL)) + assert cmd.succeeded + assert '"description": "write telegraf"' in cmd.stdout diff --git a/tasks/config.yml b/tasks/config.yml deleted file mode 100644 index 4b7d033..0000000 --- a/tasks/config.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -- name: copy config file - ansible.builtin.template: - src: influxdb.conf.j2 - dest: /etc/influxdb/influxdb.conf - owner: root - group: root - mode: 0644 - notify: restart influxdb - tags: influxdb - -- name: start and enable service - ansible.builtin.service: - name: influxdb - state: started - enabled: yes - tags: influxdb - -- name: wait http api is up - ansible.builtin.wait_for: - port: 8086 - timeout: 10 - tags: influxdb - -- name: create databases - community.general.influxdb_database: - database_name: '{{ item }}' - username: '{{ influxdb_api_user }}' - password: '{{ influxdb_api_password }}' - loop: '{{ influxdb_databases }}' - tags: influxdb - -- name: create retention policies - community.general.influxdb_retention_policy: - policy_name: '{{ item.name }}' - database_name: '{{ item.database }}' - duration: '{{ item.duration }}' - replication: '{{ item.replication|default(1) }}' - default: '{{ item.default|default(False) }}' - username: '{{ influxdb_api_user }}' - password: '{{ influxdb_api_password }}' - loop: '{{ influxdb_retention_policies }}' - tags: influxdb - -- name: create users - community.general.influxdb_user: - user_name: '{{ item.name }}' - user_password: '{{ item.password }}' - admin: '{{ item.admin|default(false) }}' - grants: '{{ item.grants|default([]) }}' - username: '{{ influxdb_api_user }}' - password: '{{ influxdb_api_password }}' - loop: '{{ influxdb_users }}' - loop_control: - label: '{{ item.name }}' - when: item.state is not defined or item.state != 'absent' - tags: influxdb - -- name: delete users - community.general.influxdb_user: - user_name: '{{ item.name }}' - username: '{{ influxdb_api_user }}' - password: '{{ influxdb_api_password }}' - state: absent - loop: '{{ influxdb_users }}' - loop_control: - label: '{{ item.name }}' - when: item.state is defined and item.state == 'absent' - tags: influxdb diff --git a/tasks/install.yml b/tasks/install.yml deleted file mode 100644 index 2c6548d..0000000 --- a/tasks/install.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- -- name: install depencies packages - ansible.builtin.package: - name: - - apt-transport-https - - collectd-core - - python3-pip - register: result - until: result is succeeded - retries: 2 - tags: influxdb - -- name: add key for influxdb repository - ansible.builtin.apt_key: - url: https://repos.influxdata.com/influxdb.key - register: result - until: result is succeeded - retries: 2 - tags: influxdb - -- name: add influxdb repository - ansible.builtin.apt_repository: - repo: 'deb https://repos.influxdata.com/debian {{ ansible_distribution_release }} stable' - tags: influxdb - -- name: install influxdb package - ansible.builtin.package: - name: - - influxdb - register: result - until: result is succeeded - retries: 2 - tags: influxdb - -- name: install python-influxdb - ansible.builtin.pip: - name: influxdb - executable: /usr/bin/pip3 - register: result - until: result is succeeded - retries: 2 - tags: influxdb diff --git a/tasks/main.yml b/tasks/main.yml index 66e9a65..1849df4 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,3 +1,103 @@ --- -- import_tasks: install.yml -- import_tasks: config.yml +- name: Install depencies packages + ansible.builtin.package: + name: + - apt-transport-https + - python3-requests + tags: influxdb + +- name: Add key for influxdb repository + ansible.builtin.get_url: + url: https://repos.influxdata.com/influxdata-archive_compat.key + dest: /etc/apt/keyrings/influx.asc + checksum: sha256:393e8779c89ac8d958f81f942f9ad7fb82a25e133faddaf92e15b16e6ac9ce4c + owner: root + group: root + mode: "0644" + tags: influxdb + +- name: Add influxdb repository + ansible.builtin.apt_repository: + repo: "deb [signed-by=/etc/apt/keyrings/influx.asc] https://repos.influxdata.com/debian stable main" + tags: influxdb + +- name: Install package + ansible.builtin.package: + name: + - influxdb2 + tags: influxdb + +- name: Start and enable service + ansible.builtin.service: + name: influxdb + state: started + enabled: true + tags: influxdb + +- name: Wait http api is up + ansible.builtin.wait_for: + port: 8086 + timeout: 10 + tags: influxdb + +- name: Setup server + influxdb_setup: + api_url: http://127.0.0.1:8086 + username: "{{ influxdb_init_username }}" + org: "{{ influxdb_init_org }}" + bucket: "{{ influxdb_init_bucket }}" + token: "{{ influxdb_api_token }}" + tags: influxdb + +- name: Manage organisations + influxdb_org: + name: "{{ item.key }}" + description: "{{ item.value.description | default(omit) }}" + api_url: http://127.0.0.1:8086 + api_token: "{{ influxdb_api_token }}" + state: "{{ item.value.state | default('present') }}" + loop: "{{ influxdb_orgs | dict2items }}" + loop_control: + label: "{{ item.key }}" + tags: influxdb + +- name: Manage buckets + influxdb_bucket: + name: "{{ item.key }}" + org: "{{ item.value.org }}" + description: "{{ item.value.description | default(omit) }}" + retention: "{{ item.value.retention | default(omit) }}" + api_url: http://127.0.0.1:8086 + api_token: "{{ influxdb_api_token }}" + state: "{{ item.value.state | default('present') }}" + loop: "{{ influxdb_buckets | dict2items }}" + loop_control: + label: "{{ item.key }}" + tags: influxdb + +- name: Manage users + influxdb_user: + name: "{{ item.key }}" + status: "{{ item.value.status | default('active') }}" + api_url: http://127.0.0.1:8086 + api_token: "{{ influxdb_api_token }}" + state: "{{ item.value.state | default('present') }}" + loop: "{{ influxdb_users | dict2items }}" + loop_control: + label: "{{ item.key }}" + tags: influxdb + +- name: Manage authorizations + influxdb_auth: + org: "{{ item.org }}" + user: "{{ item.user }}" + description: "{{ item.description | default(omit) }}" + permissions: "{{ item.permissions }}" + status: "{{ item.status | default('active') }}" + api_url: http://127.0.0.1:8086 + api_token: "{{ influxdb_api_token }}" + state: "{{ item.state | default('present') }}" + loop: "{{ influxdb_authorizations }}" + loop_control: + label: "{{ item.org }}/{{ item.user }}" + tags: influxdb