From 52785ab768b30d179b6dc201dada70f059dd4fb7 Mon Sep 17 00:00:00 2001 From: Jadyn Emma Jaeger Date: Fri, 29 Jul 2022 10:47:49 +0200 Subject: [PATCH] feat: Add ansible-test and refactor plugins --- .gitlab-ci.yml | 24 ++++ plugins/module_utils/matrix.py | 107 ++++++++++------- plugins/module_utils/synapse.py | 37 +++--- plugins/modules/matrix_login.py | 17 ++- plugins/modules/matrix_logout.py | 21 +++- plugins/modules/matrix_member.py | 158 +++++++++++++++---------- plugins/modules/matrix_notification.py | 69 +++++++---- plugins/modules/matrix_room.py | 62 +++++++--- plugins/modules/matrix_signing_key.py | 71 +++++++++-- plugins/modules/matrix_state.py | 78 ++++++++---- plugins/modules/matrix_token_login.py | 74 +++++++++--- plugins/modules/matrix_uia_login.py | 91 +++++++++----- plugins/modules/synapse_ratelimit.py | 36 ++++-- plugins/modules/synapse_register.py | 35 ++++-- requirements.txt | 3 + tests/sanity/generate-ignore.sh | 5 + tests/sanity/ignore-2.12.txt | 11 ++ tests/sanity/ignore-2.13.txt | 11 ++ tests/sanity/ignore.template | 1 + 19 files changed, 650 insertions(+), 261 deletions(-) create mode 100644 requirements.txt create mode 100644 tests/sanity/generate-ignore.sh create mode 100644 tests/sanity/ignore-2.12.txt create mode 100644 tests/sanity/ignore-2.13.txt create mode 100644 tests/sanity/ignore.template diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7f39816..2c7c01e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,31 @@ +include: + - project: 'famedly/company/devops/templates/ci-cd' + ref: 'ansible-v1' + file: '/ansible.yml' + stages: + - test - build - publish +# Debian Stable +sanity 3.9: + variables: { PYTHON_VERSION: "3.9" } + extends: .ansible-test-sanity + +# Latest stable Python +sanity 3.10: + variables: { PYTHON_VERSION: "3.10" } + extends: .ansible-test-sanity + +unit: + variables: { PYTHON_VERSION: "3.10" } + extends: .ansible-test-units + +integration: + variables: { PYTHON_VERSION: "3.10" } + extends: .ansible-test-integration + build: image: docker.io/alpine stage: build diff --git a/plugins/module_utils/matrix.py b/plugins/module_utils/matrix.py index 74d112a..6db6efb 100644 --- a/plugins/module_utils/matrix.py +++ b/plugins/module_utils/matrix.py @@ -1,93 +1,121 @@ -#!/usr/bin/python # coding: utf-8 # (c) 2021-2022, Famedly GmbH # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type -from ansible.module_utils.basic import AnsibleModule, missing_required_lib import traceback -import asyncio -#Check if all required libs can loded +from ansible.module_utils.basic import AnsibleModule, missing_required_lib + +# Check if all required libs can load LIB_IMP_ERR = None try: - from nio import * - HAS_LIB = True + from nio import AsyncClient, AsyncClientConfig, \ + Api, \ + LoginResponse, LoginError, \ + LogoutResponse, LogoutError, \ + RoomGetStateResponse, RoomGetStateError, \ + RoomBanResponse, RoomBanError, \ + RoomUnbanResponse, RoomUnbanError, \ + RoomKickResponse, RoomKickError, \ + RoomInviteResponse, RoomInviteError, \ + RoomResolveAliasResponse, RoomResolveAliasError, \ + JoinedRoomsResponse, JoinedRoomsError + HAS_LIB = True except ImportError: - LIB_IMP_ERR = traceback.format_exc() - HAS_LIB = False + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False -class AnsibleNioModule(): - def __init__(self, - custom_spec={}, + +class AnsibleNioModule: + def __init__(self, + custom_spec=None, bypass_checks=False, no_log=False, - mutually_exclusive=[['password', 'token']], + mutually_exclusive=None, required_together=None, - required_one_of=[['password', 'token', 'key']], - required_by={'password': 'user_id', 'key': 'user_id'}, + required_one_of=None, + required_by=None, add_file_common_args=False, supports_check_mode=True, required_if=None, user_logout=True): - #If a user/password login is provided, should we logout when exiting? - self.user_logout=user_logout - - #Create the Ansible module + if required_by is None: + required_by = {'password': 'user_id'} + + if required_one_of is None: + required_one_of = [['password', 'token']] + + if mutually_exclusive is None: + mutually_exclusive = [['password', 'token']] + + if custom_spec is None: + custom_spec = {} + + # If a user/password login is provided, should we logout when exiting? + self.user_logout = user_logout + + # Create the Ansible module self.module = AnsibleModule( - argument_spec = AnsibleNioModule.__common_argument_spec(custom_spec), + argument_spec=AnsibleNioModule.__common_argument_spec(custom_spec), bypass_checks=bypass_checks, no_log=no_log, mutually_exclusive=mutually_exclusive, required_together=required_together, required_one_of=required_one_of, add_file_common_args=add_file_common_args, - supports_check_mode = supports_check_mode, + supports_check_mode=supports_check_mode, required_if=required_if, required_by=required_by ) - - #Make some values from the module easly accessible - self.check_mode = self.module.check_mode - self.params = self.module.params - #Fail when matix-nio is not installed - #WARNING: We don't perform a version check! + # Make some values from the module easly accessible + self.check_mode = self.module.check_mode + self.params = self.module.params + + # Fail when matix-nio is not installed + # WARNING: We don't perform a version check! if not HAS_LIB: self.module.fail_json(msg=missing_required_lib("matrix-nio")) async def matrix_login(self): - #Login with token or supplied user account + # Login with token or supplied user account if self.module.params['token'] is None: self.client = AsyncClient(self.module.params['hs_url'], self.module.params['user_id']) - login_response = await self.client.login(self.module.params['password']) - if isinstance(login_response, LoginResponse): - self.access_token = login_response.access_token - self.device_id = login_response.device_id - else: - result['msg'] = login_response.message - result['http_status_code'] = login_response.status_code - module.fail_json(**result) + login_response = await self.client.login(password=self.module.params['password']) else: self.client = AsyncClient(self.module.params['hs_url']) - self.client.access_token = self.module.params['token'] + login_response = await self.client.login(token=self.module.params['token']) + if isinstance(login_response, LoginResponse): + self.access_token = login_response.access_token + self.device_id = login_response.device_id + else: + result = { + 'msg': login_response.message, + 'http_status_code': login_response.status_code + } + self.module.fail_json(**result) async def matrix_logout(self): if self.client.logged_in: - await self.client.logout() + request = await self.client.logout() + if isinstance(request, LogoutError): + result = {'msg': request.message} + self.module.fail_json(**result) async def exit_json(self, **result): - if self.module.params['token'] is None and self.user_logout == True: + if self.module.params['token'] is None and self.user_logout is True: await self.matrix_logout() await self.client.close() self.module.exit_json(**result) async def fail_json(self, **result): - if self.module.params['token'] is None and self.user_logout == True: + if self.module.params['token'] is None and self.user_logout is True: await self.matrix_logout() await self.client.close() self.module.fail_json(**result) @@ -101,4 +129,3 @@ class AnsibleNioModule(): token=dict(type='str', required=False, no_log=True) ) return {**argument_spec, **custom_spec} - diff --git a/plugins/module_utils/synapse.py b/plugins/module_utils/synapse.py index 923530d..e8943fa 100644 --- a/plugins/module_utils/synapse.py +++ b/plugins/module_utils/synapse.py @@ -1,4 +1,3 @@ -#!/usr/bin/python3 # coding: utf-8 # (c) 2021, Famedly GmbH @@ -8,9 +7,19 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from typing import Union, Type -import requests +import traceback import urllib.parse +from types import SimpleNamespace + +# Check if all required libs can load +LIB_IMP_ERR = None +try: + import requests + HAS_REQUESTS = True +except ImportError: + REQUESTS_IMPORT_ERROR = traceback.format_exc() + HAS_REQUESTS = False + requests = SimpleNamespace(Response=None) class AdminApi: @@ -31,39 +40,39 @@ class AdminApi: # Make API request def get(self, path: str) -> requests.Response: response = requests.get(url=urllib.parse.urljoin(self.api_url, path), - headers={"Authorization": "Bearer {}".format(self.access_token)}) + headers={"Authorization": f"Bearer {self.access_token}"}) if response.status_code == 200: return response if response.status_code == 500: raise Exceptions.MatrixException( - "Matrix Error\nHTTP-Code: {}\n Response: {}".format(response.status_code, response.text)) + f"Matrix Error\nHTTP-Code: {response.status_code}\n Response: {response.text}") else: raise Exceptions.HTTPException( - "Unexpected return code\nHTTP-Code: {}\n Response: {}".format(response.status_code, response.text)) + f"Unexpected return code\nHTTP-Code: {response.status_code}\n Response: {response.text}") def post(self, path: str, **kwargs) -> requests.Response: response = requests.post(url=urllib.parse.urljoin(self.api_url, path), - headers={"Authorization": "Bearer {}".format(self.access_token)}, **kwargs) + headers={"Authorization": f"Bearer {self.access_token}"}, **kwargs) if response.status_code == 200: return response if response.status_code == 500: raise Exceptions.MatrixException( - "Matrix Error\nHTTP-Code: {}\n Response: {}".format(response.status_code, response.text)) + f"Matrix Error\nHTTP-Code: {response.status_code}\n Response: {response.text}") else: raise Exceptions.HTTPException( - "Unexpected return code\nHTTP-Code: {}\n Response: {}".format(response.status_code, response.text)) + f"Unexpected return code\nHTTP-Code: {response.status_code}\n Response: {response.text}") def delete(self, path: str) -> requests.Response: response = requests.delete(url=urllib.parse.urljoin(self.api_url, path), - headers={"Authorization": "Bearer {}".format(self.access_token)}) + headers={"Authorization": f"Bearer {self.access_token}"}) if response.status_code == 200: return response if response.status_code == 500: raise Exceptions.MatrixException( - "Matrix Error\nHTTP-Code: {}\n Response: {}".format(response.status_code, response.text)) + f"Matrix Error\nHTTP-Code: {response.status_code}\n Response: {response.text}") else: raise Exceptions.HTTPException( - "Unexpected return code\nHTTP-Code: {}\n Response: {}".format(response.status_code, response.text)) + f"Unexpected return code\nHTTP-Code: {response.status_code}\n Response: {response.text}") @staticmethod def url_encode(string: str) -> str: @@ -83,8 +92,8 @@ class AdminApi: def set(self, user_id: str, messages_per_second: int = 0, burst_count: int = 0) -> dict: user_id = AdminApi.url_encode(user_id) return self.__parent.post( - self.API_PATH.format(user_id=user_id), json={"messages_per_second": messages_per_second, - "burst_count": burst_count}).json() + self.API_PATH.format(user_id=user_id), json={"messages_per_second": messages_per_second, + "burst_count": burst_count}).json() def delete(self, user_id: str) -> dict: user_id = AdminApi.url_encode(user_id) diff --git a/plugins/modules/matrix_login.py b/plugins/modules/matrix_login.py index 1bd5fab..2da351f 100644 --- a/plugins/modules/matrix_login.py +++ b/plugins/modules/matrix_login.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -26,14 +27,22 @@ options: description: - URL of the homeserver, where the CS-API is reachable required: true + type: str user_id: description: - The user id of the user - required: true + required: false + type: str password: description: - The password to log in with - required: true + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str requirements: - matrix-nio (Python library) ''' @@ -56,10 +65,10 @@ device_id: returned: When login was successful type: str ''' -import traceback import asyncio -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * +from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + async def run_module(): result = dict( diff --git a/plugins/modules/matrix_logout.py b/plugins/modules/matrix_logout.py index a502551..4577314 100644 --- a/plugins/modules/matrix_logout.py +++ b/plugins/modules/matrix_logout.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -26,10 +27,22 @@ options: description: - URL of the homeserver, where the CS-API is reachable required: true + type: str + user_id: + description: + - The user id of the user + required: false + type: str + password: + description: + - The password to log in with + required: false + type: str token: description: - Authentication token for the API call - required: true + required: false + type: str requirements: - matrix-nio (Python library) ''' @@ -43,10 +56,11 @@ EXAMPLES = ''' RETURN = ''' ''' -import traceback import asyncio -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * +from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + + async def run_module(): result = dict( changed=False, @@ -61,5 +75,6 @@ async def run_module(): def main(): asyncio.run(run_module()) + if __name__ == '__main__': main() diff --git a/plugins/modules/matrix_member.py b/plugins/modules/matrix_member.py index ba779cf..a2654b7 100644 --- a/plugins/modules/matrix_member.py +++ b/plugins/modules/matrix_member.py @@ -6,13 +6,10 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community' -} +ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = ''' --- @@ -20,32 +17,53 @@ author: "Johanna Dorothea Reichmann (@transcaffeine)" module: matrix_member short_description: Manage matrix room membership description: - - Manage the membership status of a given set of matrix users. Invitations (`state=member`), kicks and bans can be issued. With the `exclusive=True` flag, all other members in the room can be auto-kicked. + - Manage the membership status of a given set of matrix users. Invitations (`state=member`), + kicks and bans can be issued. With the `exclusive=True` flag, all other members in the room can be auto-kicked. options: - room_id: + hs_url: description: - - ID of the room to manage - user_ids: - description: - - List of matrix IDs to set their state + - URL of the homeserver, where the CS-API is reachable required: true - state: - description: - - In which state all listed members should be: member|kicked|banned - required: true - exclusive: - description: - - If state=member, the module ensure only the specified user_ids are in the room by kicking every other user present in the room. - required: false - token: - description: - - Authentication token for the API call. If provided, user_id and password are not required + type: str user_id: description: - The user id of the user + required: false + type: str password: description: - The password to log in with + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str + room_id: + description: + - ID of the room to edit + required: true + type: str + user_ids: + description: + - List of matrix IDs to set their state + type: list + elements: str + required: true + state: + description: + - In which state all listed members should be member|kicked|banned + choices: ['member', 'kicked', 'banned'] + type: str + required: true + exclusive: + description: + - If state=member, the module ensure only the specified user_ids are in the room by + kicking every other user present in the room. + default: false + type: bool + required: false requirements: - matrix-nio (Python library) ''' @@ -58,83 +76,97 @@ EXAMPLES = ''' room_id: "{{ matrix_room_id }}" state: member user_ids: - - @user1:matrix.org - - @user2:homeserver.tld + - "@user1:matrix.org" + - "@user2:homeserver.tld" ''' RETURN = ''' members: description: Dictionary of all members in the given room who are either invited or joined returned: When auth_token is valid - type: dict[str] + type: dict ''' -import traceback import asyncio +import traceback +from ansible.module_utils.basic import missing_required_lib + +LIB_IMP_ERR = None +try: + from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + from nio import RoomGetStateError, \ + RoomBanError, RoomUnbanError, \ + RoomKickError, RoomInviteError + HAS_LIB = True +except ImportError: + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False + + +class NioOperationError(Exception): + def __init__(self, *args): + super().__init__(*args) -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * async def get_room_members(client, room_id, res): member_resp = await client.room_get_state(room_id) if isinstance(member_resp, RoomGetStateError): - res['msg'] = "Could not get room state for roomId={0}".format(room_id) - raise Exception() + res['msg'] = f"Could not get room state for roomId={room_id}" + raise NioOperationError(res['msg']) else: - return dict(list(map(lambda m: (m['state_key'],m['content']['membership']), filter(lambda e: e['type'] == 'm.room.member' and - e['content']['membership'] in ['invite', 'join', 'leave', 'ban'], member_resp.events)))) + return dict(list(map(lambda m: (m['state_key'], m['content']['membership']), filter( + lambda e: e['type'] == 'm.room.member' and e['content']['membership'] in ['invite', 'join', 'leave', 'ban'], + member_resp.events)))) + async def ban_from_room(client, room_id, user_id, res): ban_resp = await client.room_ban(room_id, user_id) if isinstance(ban_resp, RoomBanError): - res['msg'] = "Could not ban user={0} from roomId={1}".format(user_id, room_id) - raise Exception() + res['msg'] = f"Could not ban user={user_id} from roomId={room_id}" + raise NioOperationError(res['msg']) res['changed'] = True res['banned'].append(user_id) + async def unban_from_room(client, room_id, user_id, res): ban_resp = await client.room_unban(room_id, user_id) if isinstance(ban_resp, RoomUnbanError): - res['msg'] = "Could not unban user={0} from roomId={1}".format(user_id, room_id) - raise Exception() + res['msg'] = f"Could not unban user={user_id} from roomId={room_id}" + raise NioOperationError(res['msg']) res['changed'] = True res['unbanned'].append(user_id) + async def kick_from_room(client, room_id, user_id, res): kick_resp = await client.room_kick(room_id, user_id) if isinstance(kick_resp, RoomKickError): - res['msg'] = "Could not kick user={0} from roomId={1}".format(user_id, room_id) - raise Exception() + res['msg'] = f"Could not kick user={user_id} from roomId={room_id}" + raise NioOperationError(res['msg']) res['changed'] = True res['kicked'].append(user_id) + async def invite_to_room(client, room_id, user_id, res): invite_resp = await client.room_invite(room_id, user_id) if isinstance(invite_resp, RoomInviteError): - res['msg'] = "Could not invite user={0} to roomId={1}".format(user_id, room_id) - raise Exception() + res['msg'] = f"Could not invite user={user_id} to roomId={room_id}" + raise NioOperationError(res['msg']) res['changed'] = True res['invited'].append(user_id) async def run_module(): - module_args = dict( - state=dict(choices=['member', 'kicked', 'banned'], required=True), - room_id=dict(type='str', required=True), - user_ids=dict(type='list', required=True, elements='str'), - exclusive=dict(type='bool', required=False, default=False), - ) + module_args = dict(state=dict(choices=['member', 'kicked', 'banned'], required=True), + room_id=dict(type='str', required=True), + user_ids=dict(type='list', required=True, elements='str'), + exclusive=dict(type='bool', required=False, default=False)) - result = dict( - changed=False, - banned=[], - unbanned=[], - kicked=[], - invited=[], - members=[], - msg="", - ) + result = dict(changed=False, banned=[], unbanned=[], kicked=[], invited=[], members=[], msg="", ) module = AnsibleNioModule(module_args) + if not HAS_LIB: + await module.fail_json(msg=missing_required_lib("matrix-nio")) + await module.matrix_login() if module.check_mode: @@ -151,10 +183,13 @@ async def run_module(): # Create client object client = module.client - # Query all room members (invited users count as member, as they _can_ be in the room) - room_members = await get_room_members(client, room_id, result) - present_members = {m: s for m, s in room_members.items() if s in ['join', 'invite']}.keys() - banned_members = {m: s for m, s in room_members.items() if s == 'ban'}.keys() + try: + # Query all room members (invited users count as member, as they _can_ be in the room) + room_members = await get_room_members(client, room_id, result) + present_members = {m: s for m, s in room_members.items() if s in ['join', 'invite']}.keys() + banned_members = {m: s for m, s in room_members.items() if s == 'ban'}.keys() + except NioOperationError: + await module.fail_json(**result) if not module.params['exclusive']: # Handle non-exclusive invite|kick|ban @@ -170,7 +205,7 @@ async def run_module(): await unban_from_room(client, room_id, user_id, result) elif action == 'banned' and user_id not in banned_members: await ban_from_room(client, room_id, user_id, result) - except: + except NioOperationError: await module.fail_json(**result) else: # Handle exclusive mode: get state and make lists of users to be kicked or invited @@ -182,18 +217,19 @@ async def run_module(): await invite_to_room(client, room_id, user_id, result) for user_id in to_kick: await kick_from_room(client, room_id, user_id, result) - except: + except NioOperationError: await module.fail_json(**result) # Get all current members from the room try: room_members_after = await get_room_members(client, room_id, result) result['members'] = {m: s for m, s in room_members_after.items() if s in ['join', 'invite']}.keys() - except: + except NioOperationError: pass await module.exit_json(**result) + def main(): asyncio.run(run_module()) diff --git a/plugins/modules/matrix_notification.py b/plugins/modules/matrix_notification.py index 2eccb6f..54735ce 100644 --- a/plugins/modules/matrix_notification.py +++ b/plugins/modules/matrix_notification.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -21,33 +22,43 @@ module: matrix_notification short_description: Send notifications to matrix description: - This module sends html formatted notifications to matrix rooms. -version_added: "2.8" +version_added: "2.8.0" options: - msg_plain: - description: - - Plain text form of the message to send to matrix, usually markdown - required: true - msg_html: - description: - - HTML form of the message to send to matrix - required: true - room_id: - description: - - ID of the room to send the notification to - required: true hs_url: description: - URL of the homeserver, where the CS-API is reachable required: true - token: - description: - - Authentication token for the API call. If provided, user_id and password are not required + type: str user_id: description: - The user id of the user + required: false + type: str password: description: - The password to log in with + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str + msg_plain: + description: + - Plain text form of the message to send to matrix, usually markdown + required: true + type: str + msg_html: + description: + - HTML form of the message to send to matrix + required: true + type: str + room_id: + description: + - ID of the room to send the notification to + required: true + type: str requirements: - matrix-nio (Python library) ''' @@ -73,16 +84,27 @@ EXAMPLES = ''' RETURN = ''' ''' -import traceback import asyncio -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * +import traceback + +from ansible.module_utils.basic import missing_required_lib + +LIB_IMP_ERR = None +try: + from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + from nio import RoomSendResponse, RoomSendError + HAS_LIB = True +except ImportError: + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False + async def run_module(): module_args = dict( msg_plain=dict(type='str', required=True), msg_html=dict(type='str', required=True), room_id=dict(type='str', required=True), - ) + ) result = dict( changed=False, @@ -90,15 +112,17 @@ async def run_module(): ) module = AnsibleNioModule(module_args) + if not HAS_LIB: + await module.fail_json(msg=missing_required_lib("matrix-nio")) if module.check_mode: return result - + await module.matrix_login() client = module.client # send message - await client.room_send( + response = await client.room_send( room_id=module.params['room_id'], message_type="m.room.message", content={ @@ -108,9 +132,12 @@ async def run_module(): "formatted_body": module.params['msg_html'], } ) + if isinstance(response, RoomSendError): + await module.fail_json(**result) await module.exit_json(**result) + def main(): asyncio.run(run_module()) diff --git a/plugins/modules/matrix_room.py b/plugins/modules/matrix_room.py index e121e59..97aaeff 100644 --- a/plugins/modules/matrix_room.py +++ b/plugins/modules/matrix_room.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -20,26 +21,37 @@ author: "Jan Christian Grünhage (@jcgruenhage)" module: matrix_room short_description: Join/Create matrix room description: - - This module takes a room alias and makes sure that the user identified by the access token is in such a room. If that room does not exist, it is created, if it does exist but the user is not in it, it tries to join. If the alias is taken and the user can't join the room, the module will fail. Remote aliases are not supported for creating, but work for joining. + - This module takes a room alias and makes sure that the user identified by the access token is in such a room. + If that room does not exist, it is created, if it does exist but the user is not in it, it tries to join. + If the alias is taken and the user can't join the room, the module will fail. + Remote aliases are not supported for creating, but work for joining. options: - alias: - description: - - Alias of the room to join/create - required: true hs_url: description: - URL of the homeserver, where the CS-API is reachable required: true - token: - description: - - Authentication token for the API call. If provided, user_id and password are not required + type: str user_id: description: - The user id of the user + required: false + type: str password: description: - The password to log in with -equirements: + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str + alias: + description: + - Alias of the room to join/create + required: true + type: str +requirements: - matrix-nio (Python library) ''' @@ -55,12 +67,28 @@ RETURN = ''' room_id: description: ID of the room type: str - sample: !asdfbuiarbk213e479asf:server.tld + returned: success + sample: "!asdfbuiarbk213e479asf:server.tld" ''' -import traceback + import asyncio import re -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * +import traceback + +from ansible.module_utils.basic import missing_required_lib + +LIB_IMP_ERR = None +try: + from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + from nio import RoomCreateResponse, RoomCreateError, \ + JoinedRoomsResponse, JoinedRoomsError, \ + JoinResponse, JoinError, \ + RoomResolveAliasResponse, RoomResolveAliasError + HAS_LIB = True +except ImportError: + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False + async def run_module(): module_args = dict( @@ -73,6 +101,9 @@ async def run_module(): ) module = AnsibleNioModule(module_args) + if not HAS_LIB: + await module.fail_json(msg=missing_required_lib("matrix-nio")) + await module.matrix_login() client = module.client @@ -87,7 +118,7 @@ async def run_module(): rooms_resp = await client.joined_rooms() if isinstance(rooms_resp, JoinedRoomsError): failed = True - result = {"msg":"Couldn't get joined rooms."} + result = {"msg": "Couldn't get joined rooms."} elif room_id_resp.room_id in rooms_resp.rooms: result = {"room_id": room_id_resp.room_id, "changed": False} else: @@ -99,7 +130,7 @@ async def run_module(): result = {"room_id": join_resp.room_id, "changed": True} else: failed = True - result = {"msg": "Room exists, but couldn't join: {1}".format(join_resp)} + result = {"msg": f"Room exists, but couldn't join: {join_resp}"} else: # Get local part of alias local_part_regex = re.search("#([^:]*):(.*)", module.params['alias']) @@ -113,13 +144,14 @@ async def run_module(): result = {"room_id": create_room_resp.room_id, "changed": True} else: failed = True - result = {"msg": "Room does not exist but couldn't be created either: {0}".format(create_room_resp)} + result = {"msg": f"Room does not exist but couldn't be created either: {create_room_resp}"} if failed: await module.fail_json(**result) else: await module.exit_json(**result) + def main(): asyncio.run(run_module()) diff --git a/plugins/modules/matrix_signing_key.py b/plugins/modules/matrix_signing_key.py index b1abc8d..b9f3ef1 100644 --- a/plugins/modules/matrix_signing_key.py +++ b/plugins/modules/matrix_signing_key.py @@ -1,18 +1,65 @@ -#!/bin/python3 -# Copyright: (c) 2018, Emmanouil Kampitakis +#!/usr/bin/python +# Copyright: (c) 2018 # Apache 2.0 +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} + +DOCUMENTATION = ''' +--- +author: "Emmanouil Kampitakis (@madonius)" +module: matrix_signing_key +short_description: Create a signing key file if not exists +description: + - Create a signing key file if not exists +options: + path: + description: + - Path to the signing key file + required: true + type: str +requirements: + - signedjson (Python library) +''' + +EXAMPLES = ''' +- name: Create signing key file + matrix_signing_key: + changed: "/path/to/file" +''' + +RETURN = ''' +''' import os -from ansible.module_utils.basic import AnsibleModule -from signedjson import key +import traceback + +from ansible.module_utils.basic import AnsibleModule, missing_required_lib + +# Check if all required libs can load +LIB_IMP_ERR = None +try: + from signedjson import key + HAS_LIB = True +except ImportError: + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False + def write_signing_key(path): - with open(path,'w') as file: + with open(path, 'w') as file: key.write_signing_keys( - file, - [key.generate_signing_key('first')] + file, + [key.generate_signing_key('first')] ) + def run_module(): module_args = dict( path=dict(type='str', required=True), @@ -29,6 +76,9 @@ def run_module(): supports_check_mode=True ) + if not HAS_LIB: + module.fail_json(msg=missing_required_lib("signedjson")) + signing_key_path = module.params['path'] signing_key_exists = os.path.isfile(signing_key_path) @@ -37,12 +87,17 @@ def run_module(): result['changed'] = True if module.check_mode: module.exit_json(**result) - write_signing_key(signing_key_path) + try: + write_signing_key(signing_key_path) + except OSError as e: + module.fail_json(msg=str(e)) module.exit_json(**result) + def main(): run_module() + if __name__ == '__main__': main() diff --git a/plugins/modules/matrix_state.py b/plugins/modules/matrix_state.py index 54314a0..b65f84a 100644 --- a/plugins/modules/matrix_state.py +++ b/plugins/modules/matrix_state.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -22,36 +23,47 @@ short_description: Set matrix room state description: - This module sets matrix room state idempotently options: - event_type: - description: - - Event type of the state to be set - required: true - state_key: - description: - - State key for the state event to be set - required: true - content: - description: - - The content to set the state to - required: true - room_id: - description: - - ID of the room to set the state for - required: true hs_url: description: - URL of the homeserver, where the CS-API is reachable required: true - token: - description: - - Authentication token for the API call. If provided, user_id and password are not required + type: str user_id: description: - The user id of the user + required: false + type: str password: description: - The password to log in with -equirements: + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str + event_type: + description: + - Event type of the state to be set + required: true + type: str + state_key: + description: + - State key for the state event to be set + required: true + type: str + content: + description: + - The content to set the state to + required: true + type: dict + room_id: + description: + - ID of the room to set the state for + required: true + type: str +requirements: - matrix-client (Python library) ''' @@ -79,14 +91,27 @@ event_id: type: str sample: $Het2Dv7EEDFNJNgY-ehLSUrdqMo8JOxZDCMnuQPSNfo ''' -import traceback import asyncio -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * +import traceback + +from ansible.module_utils.basic import missing_required_lib + +LIB_IMP_ERR = None +try: + from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + from nio import JoinedRoomsResponse, JoinedRoomsError, \ + RoomGetStateEventResponse, RoomPutStateResponse + + HAS_LIB = True +except ImportError: + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False + async def run_module(): module_args = dict( event_type=dict(type='str', required=True), - state_key=dict(type='str', required=True), + state_key=dict(type='str', required=True, no_log=False), content=dict(type='dict', required=True), room_id=dict(type='str', required=True) ) @@ -97,6 +122,8 @@ async def run_module(): ) module = AnsibleNioModule(module_args) + if not HAS_LIB: + await module.fail_json(msg=missing_required_lib("matrix-nio")) await module.matrix_login() client = module.client @@ -110,7 +137,7 @@ async def run_module(): rooms_resp = await client.joined_rooms() if isinstance(rooms_resp, JoinedRoomsError): failed = True - result = {"msg":"Couldn't get joined rooms."} + result = {"msg": "Couldn't get joined rooms."} elif module.params['room_id'] not in rooms_resp.rooms: failed = True result = {"msg": "Not in the room you're trying to set state for."} @@ -138,13 +165,14 @@ async def run_module(): # Else, fail else: failed = True - result = {"msg": "Couldn't set state: {error}".format(error=send_resp)} + result = {"msg": f"Couldn't set state: {send_resp}"} if failed: await module.fail_json(**result) else: await module.exit_json(**result) + def main(): asyncio.run(run_module()) diff --git a/plugins/modules/matrix_token_login.py b/plugins/modules/matrix_token_login.py index 678b20f..50fdd3f 100644 --- a/plugins/modules/matrix_token_login.py +++ b/plugins/modules/matrix_token_login.py @@ -7,6 +7,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -17,7 +18,7 @@ ANSIBLE_METADATA = { DOCUMENTATION = ''' --- -author: "Jan Christian Grünhage" +author: "Jan Christian Grünhage (@jcgruenhage)" module: matrix_token_login short_description: Use com.famedly.token based logins to obtain an access token description: @@ -27,17 +28,30 @@ options: description: - URL of the homeserver, where the CS-API is reachable required: true + type: str user_id: description: - The user id of the user - required: true - key: + required: false + type: str + password: description: - - The key to sign the log in token with - required: true + - The password to log in with + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str admin: description: - Whether to set the user as admin during login + type: bool + key: + description: Login key to use + type: str + required: true requirements: - matrix-nio (Python library) - jwcrypto (Python library) @@ -62,18 +76,38 @@ device_id: returned: When login was successful type: str ''' -import traceback import asyncio import json import base64 -from jwcrypto import jwt, jwk import time +import traceback + +from ansible.module_utils.basic import missing_required_lib + +# Check if all required libs can load +JWCRYPTO_IMP_ERR = None +try: + from jwcrypto import jwt, jwk + + HAS_JWCRYPTO = True +except ImportError: + JWCRYPTO_IMP_ERR = traceback.format_exc() + HAS_JWCRYPTO = False + +NIO_IMP_ERR = None +try: + from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + from nio import AsyncClient, Api + + HAS_NIO = True +except ImportError: + NIO_IMP_ERR = traceback.format_exc() + HAS_NIO = False -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * async def run_module(): module_args = dict( - key=dict(type='str', required=True), + key=dict(type='str', required=True, no_log=True), admin=dict(type='bool', required=False), ) @@ -83,6 +117,10 @@ async def run_module(): ) module = AnsibleNioModule(module_args, user_logout=False) + if not HAS_JWCRYPTO: + await module.fail_json(msg=missing_required_lib("jwcrypto")) + if not HAS_NIO: + await module.fail_json(msg=missing_required_lib("matrix-nio")) if module.check_mode: return result @@ -112,9 +150,9 @@ async def run_module(): } key = jwk.JWK(**key) claims = { - "iss": "Matrix UIA Login Ansible Module", - "sub": client.user, - "exp": int(time.time()) + 60 * 30, + "iss": "Matrix UIA Login Ansible Module", + "sub": client.user, + "exp": int(time.time()) + 60 * 30, } if admin is not None: @@ -126,12 +164,12 @@ async def run_module(): token.make_signed_token(key) auth = { - "type": "com.famedly.login.token", - "identifier" : { - "type": "m.id.user", - "user": client.user - }, - "token": token.serialize() + "type": "com.famedly.login.token", + "identifier": { + "type": "m.id.user", + "user": client.user + }, + "token": token.serialize() } payload = json.dumps(auth) diff --git a/plugins/modules/matrix_uia_login.py b/plugins/modules/matrix_uia_login.py index e647bdb..6c660cd 100644 --- a/plugins/modules/matrix_uia_login.py +++ b/plugins/modules/matrix_uia_login.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -26,14 +27,22 @@ options: description: - URL of the homeserver, where the CS-API is reachable required: true + type: str user_id: description: - The user id of the user - required: true + required: false + type: str password: description: - The password to log in with - required: true + required: false + type: str + token: + description: + - Authentication token for the API call + required: false + type: str requirements: - matrix-nio (Python library) ''' @@ -56,14 +65,24 @@ device_id: returned: When login was successful type: str ''' -import traceback import asyncio -from functools import reduce import json +import traceback + +from ansible.module_utils.basic import missing_required_lib + +LIB_IMP_ERR = None +try: + from ansible_collections.famedly.matrix.plugins.module_utils.matrix import AnsibleNioModule + from nio import AsyncClient, Api + HAS_LIB = True +except ImportError: + LIB_IMP_ERR = traceback.format_exc() + HAS_LIB = False -from ansible_collections.famedly.matrix.plugins.module_utils.matrix import * log = [] + def get_payload(data): payload = json.dumps(data) payload_length = len(payload) @@ -73,55 +92,62 @@ def get_payload(data): } return payload, headers + async def do_password_stage(client, session, method, path, password): auth = { - "type": "m.login.password", - "identifier" : { - "type": "m.id.user", - "user": client.user - }, - "session": session, - "password": password + "type": "m.login.password", + "identifier": { + "type": "m.id.user", + "user": client.user + }, + "session": session, + "password": password } - log.append("DEBUG: attempt stage=" + auth['type'] + " for session="+ auth['session'] + " with password="+auth['password'] + ", method=" + method) - payload, headers = get_payload({"auth" : auth }) - raw_response = await client.send(method, path, payload, headers) - res = await client.parse_body(raw_response) - log.append("DEBUG: stage=" + auth['type'] + " resulted in status=" + str(raw_response.status)) - return raw_response.status, res - -async def do_dummy_stage(client, session, method, path, password): - auth = { - "type": "m.login.dummy", - "session": session - } - log.append("DEBUG: attempt stage=" + auth['type'] + " for session="+ auth['session']) + log.append(f"DEBUG: attempt stage={auth['type']} for session={auth['session']} with password={auth['password']}, method={method}") payload, headers = get_payload({"auth": auth}) raw_response = await client.send(method, path, payload, headers) res = await client.parse_body(raw_response) - log.append("DEBUG: stage=" + auth['type'] + " resulted in status=" + str(raw_response.status)) + log.append(f"DEBUG: stage={auth['type']} resulted in status={raw_response.status}") return raw_response.status, res + +async def do_dummy_stage(client, session, method, path, password): + auth = { + "type": "m.login.dummy", + "session": session + } + log.append(f"DEBUG: attempt stage={auth['type']} for session={auth['session']}") + payload, headers = get_payload({"auth": auth}) + raw_response = await client.send(method, path, payload, headers) + res = await client.parse_body(raw_response) + log.append(f"DEBUG: stage{auth['type']} resulted in status={raw_response.status}") + return raw_response.status, res + + uia_stages = { "m.login.password": do_password_stage, "m.login.dummy": do_dummy_stage } + # Picks the best compatible flow out of an array of flows def pick_flow(flows): supported_stages = uia_stages.keys() # reduces each flow to a boolean telling filter if the flow consists only out of compatible stages - compatible_flows = [flow for flow in flows if all([stage in supported_stages for stage in flow['stages']])] + compatible_flows = [flow for flow in flows if all(stage in supported_stages for stage in flow['stages'])] # the best flow is the one with the fewest stages, key= takes a function telling min() the weight of an entry best = min(compatible_flows, key=(lambda flow: len(flow['stages']))) return best + async def run_module(): result = dict( changed=False, ) module = AnsibleNioModule(user_logout=False) + if not HAS_LIB: + await module.fail_json(msg=missing_required_lib("matrix-nio")) if module.check_mode: return result @@ -134,6 +160,7 @@ async def run_module(): # Collect and check login information password = module.params['password'] + token = module.params['token'] if password is None and token is None: await module.fail_json(msg="A PASSWORD has to be provided") @@ -149,21 +176,21 @@ async def run_module(): raw_response = await client.send(method, path, {}) res = await client.parse_body(raw_response) uia_session = res['session'] - log.append("DEBUG: begin UIA for session=" + uia_session) + log.append(f"DEBUG: begin UIA for session={uia_session}") # Figure out best compatible UIA login flow - log.append("INFO: available flows: " + str(res['flows'])) + log.append(f"INFO: available flows: {res['flows']}") flow_to_attempt = pick_flow(res['flows']) - log.append("INFO: picking flow: " + (" -> ".join(flow_to_attempt['stages']))) + log.append(f"INFO: picking flow: {' -> '.join(flow_to_attempt['stages'])}") # Attempt each stage in the flow for stage in flow_to_attempt['stages']: stage_status, stage_result = await uia_stages[stage](client, uia_session, method, path, password) if int(stage_status) == 401 and stage != (flow_to_attempt['stages'])[-1]: - log.append("INFO: completed stage " + stage) + log.append(f"INFO: completed stage {stage}") completed_stages = stage_result['completed'] elif int(stage_status) == 200 and stage == (flow_to_attempt['stages'])[-1]: - log.append("INFO: final stage completed " + stage) + log.append(f"INFO: final stage completed {stage}") result['token'] = stage_result['access_token'] result['device_id'] = stage_result['device_id'] failed = False diff --git a/plugins/modules/synapse_ratelimit.py b/plugins/modules/synapse_ratelimit.py index ef4a88c..61b8845 100644 --- a/plugins/modules/synapse_ratelimit.py +++ b/plugins/modules/synapse_ratelimit.py @@ -16,7 +16,7 @@ ANSIBLE_METADATA = { DOCUMENTATION = ''' --- -author: "Jadyn Emma Jäger (@jadyn.dev)" +author: "Jadyn Emma Jäger (@jadyndev)" module: synapse_ratelimit short_description: Change a users rate-limits description: @@ -26,31 +26,34 @@ options: description: - URL of the homeserver, where the CS-API is reachable required: true + type: str access_token: description: - Shared secret to authenticate registration request required: true + type: str user_id: description: - The fully qualified MXID of a __local__ user required: true + type: str action: description: - Which (http) operation should be executed - required: True + required: false type: str - choices: 'get', 'set', 'delete' + choices: ['get', 'set', 'delete'] default: 'get' messages_per_second: description: - Set the maximum messages per second (0 = disabled) - required: False + required: false type: int default: 0 burst_count: description: - Set the maximum message burst (0 = disabled) - required: False + required: false type: int default: 0 requirements: [] @@ -69,13 +72,21 @@ EXAMPLES = ''' RETURN = ''' ratelimit: - - burst_count: 5 - - messages_per_second: 10 -if a ratelimit is set, otherwise `ratelimit` is empty + description: if a ratelimit is set, otherwise `ratelimit` is empty + type: dict + returned: success + sample: + burst_count: 5 + messages_per_second: 10 ''' -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.famedly.matrix.plugins.module_utils.synapse import * +from ansible.module_utils.basic import AnsibleModule, missing_required_lib +import traceback + +try: + from ansible_collections.famedly.matrix.plugins.module_utils.synapse import AdminApi, Exceptions, HAS_REQUESTS +except ImportError: + REQUESTS_IMPORT_ERROR = traceback.format_exc() def main(): @@ -97,6 +108,9 @@ def main(): supports_check_mode=True ) + if not HAS_REQUESTS: + module.fail_json(msg=missing_required_lib("requests")) + if module.check_mode: return result @@ -117,7 +131,7 @@ def main(): result['ratelimit'] = synapse.ratelimit.delete(module.params['user_id']) result['changed'] = ratelimit != result['ratelimit'] module.exit_json(**result) - raise NotImplementedError("action {} is not implemented".format(action)) + raise NotImplementedError(f"action {action} is not implemented") except (Exceptions.HTTPException, Exceptions.MatrixException) as e: result['msg'] = str(e) module.fail_json(**result) diff --git a/plugins/modules/synapse_register.py b/plugins/modules/synapse_register.py index 43c0b69..67b0bc2 100644 --- a/plugins/modules/synapse_register.py +++ b/plugins/modules/synapse_register.py @@ -6,6 +6,7 @@ # GNU Affero General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/agpl-3.0.txt) from __future__ import (absolute_import, division, print_function) + __metaclass__ = type ANSIBLE_METADATA = { @@ -26,14 +27,17 @@ options: description: - URL of the homeserver, where the CS-API is reachable required: true + type: str user_id: description: - The user id of the user required: true + type: str password: description: - The password to register with required: true + type: str admin: description: - Whether or not the new user should be an admin @@ -44,6 +48,7 @@ options: description: - Shared secret to authenticate registration request required: true + type: str requirements: [] ''' @@ -57,21 +62,28 @@ EXAMPLES = ''' shared_secret: "long secret string" ''' -RETURN = ''' -''' -import traceback import asyncio import hmac import hashlib -import requests +import traceback + +# Check if all required libs can load +LIB_IMP_ERR = None +try: + import requests + + HAS_REQUESTS = True +except ImportError: + REQUESTS_IMPORT_ERROR = traceback.format_exc() + HAS_REQUESTS = False from ansible.module_utils.basic import AnsibleModule, missing_required_lib -def generate_mac(nonce, shared_secret, user, password, admin=False, user_type=None): +def generate_mac(nonce, shared_secret, user, password, admin=False, user_type=None): mac = hmac.new( - key=shared_secret.encode('utf8'), - digestmod=hashlib.sha1, + key=shared_secret.encode('utf8'), + digestmod=hashlib.sha1, ) mac.update(nonce.encode('utf8')) @@ -87,6 +99,7 @@ def generate_mac(nonce, shared_secret, user, password, admin=False, user_type=No return mac.hexdigest() + async def run_module(): module_args = dict( hs_url=dict(type='str', required=True), @@ -105,18 +118,22 @@ async def run_module(): supports_check_mode=True ) + if not HAS_REQUESTS: + module.fail_json(msg=missing_required_lib("requests")) + if module.check_mode: return result failed = False - url = "{}/_synapse/admin/v1/register".format(module.params["hs_url"]) + url = f"{module.params['hs_url']}/_synapse/admin/v1/register" response = requests.get(url) if response.status_code != 200: result["msg"] = response.json()["error"] module.exit_json(**result) nonce = response.json()["nonce"] - mac = generate_mac(nonce, module.params["shared_secret"], module.params["user_id"], module.params["password"], module.params["admin"]) + mac = generate_mac(nonce, module.params["shared_secret"], module.params["user_id"], module.params["password"], + module.params["admin"]) data = { "nonce": nonce, diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4c7bf61 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ansible~=5.2.0 +matrix-nio~=0.19.0 +signedjson~=1.1.4 \ No newline at end of file diff --git a/tests/sanity/generate-ignore.sh b/tests/sanity/generate-ignore.sh new file mode 100644 index 0000000..9db0c59 --- /dev/null +++ b/tests/sanity/generate-ignore.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +cd ../../ +while read -r line; do + find plugins/modules -name "*.py" | xargs -I {} -n 1 printf "{} $line\n" +done <"tests/sanity/ignore.template" diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt new file mode 100644 index 0000000..ab8e8d5 --- /dev/null +++ b/tests/sanity/ignore-2.12.txt @@ -0,0 +1,11 @@ +plugins/modules/matrix_signing_key.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/synapse_register.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_notification.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_state.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_room.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_uia_login.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/synapse_ratelimit.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_token_login.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_login.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_logout.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_member.py validate-modules:missing-gplv3-license # ignore license check diff --git a/tests/sanity/ignore-2.13.txt b/tests/sanity/ignore-2.13.txt new file mode 100644 index 0000000..ab8e8d5 --- /dev/null +++ b/tests/sanity/ignore-2.13.txt @@ -0,0 +1,11 @@ +plugins/modules/matrix_signing_key.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/synapse_register.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_notification.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_state.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_room.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_uia_login.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/synapse_ratelimit.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_token_login.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_login.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_logout.py validate-modules:missing-gplv3-license # ignore license check +plugins/modules/matrix_member.py validate-modules:missing-gplv3-license # ignore license check diff --git a/tests/sanity/ignore.template b/tests/sanity/ignore.template new file mode 100644 index 0000000..9d4f69e --- /dev/null +++ b/tests/sanity/ignore.template @@ -0,0 +1 @@ +validate-modules:missing-gplv3-license # ignore license check