Matrix Docker Ansible eploy
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

258 строки
14 KiB

  1. # SPDX-FileCopyrightText: 2026 Slavi Pantaleev
  2. #
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. ---
  5. # Proves the bridge starts, reads the role-rendered configuration and registration, migrates
  6. # its Postgres schema, and opens its appservice listener. No Telegram login is attempted.
  7. - name: Verify mautrix-telegram
  8. hosts: all
  9. become: true
  10. vars_files:
  11. - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml"
  12. - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml"
  13. # These are deliberately lazy rather than set_fact. The bridge configuration contains Go
  14. # templates; storing the parsed document as a fact would make Ansible template them again.
  15. vars:
  16. mautrix_telegram_config: "{{ mautrix_telegram_config_file.content | b64decode | from_yaml }}"
  17. mautrix_telegram_registration: "{{ mautrix_telegram_registration_file.content | b64decode | from_yaml }}"
  18. mautrix_telegram_labels_rendered: "{{ mautrix_telegram_labels.content | b64decode }}"
  19. gather_facts: false
  20. tasks:
  21. # Load the shipped version instead of pinning it in the scenario, so Renovate bumps are
  22. # checked against the new image automatically.
  23. - name: Load the role's defaults under a separate name
  24. ansible.builtin.include_vars:
  25. file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
  26. name: mautrix_telegram_role_defaults
  27. - name: Wait for the mautrix-telegram service to become active
  28. ansible.builtin.systemd_service:
  29. name: matrix-mautrix-telegram.service
  30. register: mautrix_telegram_service
  31. until: mautrix_telegram_service.status.ActiveState == 'active'
  32. retries: 30
  33. delay: 5
  34. failed_when: false
  35. # Restart=always leaves a crash-looping service active, so both properties matter.
  36. - name: Assert the service is active and has not been restarting
  37. ansible.builtin.assert:
  38. that:
  39. - mautrix_telegram_service.status.ActiveState == 'active'
  40. - mautrix_telegram_service.status.NRestarts is defined
  41. - mautrix_telegram_service.status.NRestarts | int == 0
  42. fail_msg: >-
  43. matrix-mautrix-telegram.service is
  44. {{ mautrix_telegram_service.status.ActiveState | default('unknown') }}
  45. after {{ mautrix_telegram_service.status.NRestarts | default('?') }} restart(s)
  46. success_msg: "matrix-mautrix-telegram.service is active and has not restarted"
  47. # /live proves the listener opened; /ready additionally proves startup passed the
  48. # homeserver identity check and database initialization.
  49. - name: Wait for the bridge liveness endpoint
  50. ansible.builtin.command:
  51. argv:
  52. - docker
  53. - run
  54. - --rm
  55. - --network={{ matrix_bridge_mautrix_telegram_container_network }}
  56. - "{{ molecule_shared_image_curl }}"
  57. - --silent
  58. - --output
  59. - /dev/null
  60. - --write-out
  61. - "HTTP_STATUS=%{http_code}"
  62. - http://matrix-mautrix-telegram:8080/_matrix/mau/live
  63. register: mautrix_telegram_live
  64. changed_when: false
  65. until: "'HTTP_STATUS=200' in mautrix_telegram_live.stdout"
  66. retries: 24
  67. delay: 5
  68. failed_when: false
  69. - name: Wait for the bridge readiness endpoint
  70. ansible.builtin.command:
  71. argv:
  72. - docker
  73. - run
  74. - --rm
  75. - --network={{ matrix_bridge_mautrix_telegram_container_network }}
  76. - "{{ molecule_shared_image_curl }}"
  77. - --silent
  78. - --output
  79. - /dev/null
  80. - --write-out
  81. - "HTTP_STATUS=%{http_code}"
  82. - http://matrix-mautrix-telegram:8080/_matrix/mau/ready
  83. register: mautrix_telegram_ready
  84. changed_when: false
  85. until: "'HTTP_STATUS=200' in mautrix_telegram_ready.stdout"
  86. retries: 24
  87. delay: 5
  88. failed_when: false
  89. - name: Assert the bridge is live and ready on its appservice port
  90. ansible.builtin.assert:
  91. that:
  92. - "'HTTP_STATUS=200' in mautrix_telegram_live.stdout"
  93. - "'HTTP_STATUS=200' in mautrix_telegram_ready.stdout"
  94. fail_msg: >-
  95. The appservice health responses were live={{ mautrix_telegram_live.stdout | default('none') }}
  96. and ready={{ mautrix_telegram_ready.stdout | default('none') }}
  97. success_msg: "The bridge is live and ready on its appservice port"
  98. - name: Read the configuration the role rendered
  99. ansible.builtin.slurp:
  100. src: "{{ matrix_bridge_mautrix_telegram_config_path }}/config.yaml"
  101. register: mautrix_telegram_config_file
  102. # All scenario-provided values below differ from the role or component defaults.
  103. - name: Assert the parsed configuration carries the scenario's values
  104. ansible.builtin.assert:
  105. that:
  106. - mautrix_telegram_config.homeserver.address == matrix_bridge_mautrix_telegram_homeserver_address
  107. - mautrix_telegram_config.homeserver.domain == matrix_bridge_mautrix_telegram_homeserver_domain
  108. - mautrix_telegram_config.network.api_id | int == matrix_bridge_mautrix_telegram_api_id | int
  109. - mautrix_telegram_config.network.api_hash == matrix_bridge_mautrix_telegram_api_hash
  110. - mautrix_telegram_config.appservice.bot.username == matrix_bridge_mautrix_telegram_appservice_bot_username
  111. - mautrix_telegram_config.appservice.as_token == matrix_bridge_mautrix_telegram_appservice_token
  112. - mautrix_telegram_config.appservice.hs_token == matrix_bridge_mautrix_telegram_homeserver_token
  113. - mautrix_telegram_config.bridge.command_prefix == matrix_bridge_mautrix_telegram_command_prefix
  114. - not mautrix_telegram_config.bridge.personal_filtering_spaces
  115. - not mautrix_telegram_config.matrix.federate_rooms
  116. - not mautrix_telegram_config.backfill.enabled
  117. - mautrix_telegram_config.logging.min_level == matrix_bridge_mautrix_telegram_logging_level
  118. - "'(Molecule)' in mautrix_telegram_config.network.displayname_template"
  119. - mautrix_telegram_config.bridge.permissions[matrix_bridge_mautrix_telegram_homeserver_domain] == 'user'
  120. fail_msg: "The parsed configuration does not carry the scenario's values"
  121. success_msg: "The parsed configuration carries the scenario's values"
  122. - name: Assert the configuration points at the scenario's Postgres database
  123. ansible.builtin.assert:
  124. that:
  125. - mautrix_telegram_config.database.type == matrix_bridge_mautrix_telegram_database_engine
  126. - matrix_bridge_mautrix_telegram_database_username in mautrix_telegram_config.database.uri
  127. - matrix_bridge_mautrix_telegram_database_name in mautrix_telegram_config.database.uri
  128. - matrix_bridge_mautrix_telegram_database_hostname in mautrix_telegram_config.database.uri
  129. fail_msg: >-
  130. database.uri is {{ mautrix_telegram_config.database.uri | default('unset') }},
  131. which was not built from the scenario's connection settings
  132. success_msg: "The configuration points at the scenario's Postgres database"
  133. - name: Assert the exposure settings produce the bridge's public address
  134. ansible.builtin.assert:
  135. that:
  136. - mautrix_telegram_config.appservice.public_address == 'https://bridges.molecule.local/bridges/telegram'
  137. fail_msg: >-
  138. appservice.public_address is
  139. {{ mautrix_telegram_config.appservice.public_address | default('unset') }}
  140. success_msg: "The exposure settings produce the bridge's public address"
  141. - name: Read the appservice registration the role rendered
  142. ansible.builtin.slurp:
  143. src: "{{ matrix_bridge_mautrix_telegram_config_path }}/registration.yaml"
  144. register: mautrix_telegram_registration_file
  145. - name: Assert the parsed registration carries the scenario's identity and tokens
  146. ansible.builtin.assert:
  147. that:
  148. - mautrix_telegram_registration.id == 'telegram'
  149. - mautrix_telegram_registration.as_token == matrix_bridge_mautrix_telegram_appservice_token
  150. - mautrix_telegram_registration.hs_token == matrix_bridge_mautrix_telegram_homeserver_token
  151. - mautrix_telegram_registration.sender_localpart == '_bot_' + matrix_bridge_mautrix_telegram_appservice_bot_username
  152. - mautrix_telegram_registration.url == 'http://matrix-mautrix-telegram:8080'
  153. fail_msg: "The appservice registration does not carry the scenario's identity and tokens"
  154. success_msg: "The appservice registration carries the scenario's identity and tokens"
  155. # Check the namespace regexes by matching examples rather than reimplementing their escaping.
  156. - name: Assert the registration namespaces cover the bot and Telegram ghost users
  157. ansible.builtin.assert:
  158. that:
  159. - mautrix_telegram_ghost_regex | length > 0
  160. - mautrix_telegram_bot_regex | length > 0
  161. - mautrix_telegram_ghost_mxid is match(mautrix_telegram_ghost_regex)
  162. - mautrix_telegram_non_ghost_mxid is not match(mautrix_telegram_ghost_regex)
  163. - mautrix_telegram_bot_mxid is match(mautrix_telegram_bot_regex)
  164. fail_msg: "The registration namespaces do not cover the bot and Telegram ghost users"
  165. success_msg: "The registration namespaces cover the bot and Telegram ghost users"
  166. vars:
  167. mautrix_telegram_user_regexes: "{{ mautrix_telegram_registration.namespaces.users | map(attribute='regex') | list }}"
  168. mautrix_telegram_ghost_regex: "{{ mautrix_telegram_user_regexes | select('search', 'telegram_') | first | default('') }}"
  169. mautrix_telegram_bot_regex: "{{ mautrix_telegram_user_regexes | reject('search', 'telegram_') | first | default('') }}"
  170. mautrix_telegram_ghost_mxid: "@telegram_12345678:{{ matrix_bridge_mautrix_telegram_homeserver_domain }}"
  171. mautrix_telegram_non_ghost_mxid: "@signal_12345678:{{ matrix_bridge_mautrix_telegram_homeserver_domain }}"
  172. mautrix_telegram_bot_mxid: "@{{ matrix_bridge_mautrix_telegram_appservice_bot_username }}:{{ matrix_bridge_mautrix_telegram_homeserver_domain }}"
  173. # Tables can appear only after hostname resolution, authentication and migrations succeed.
  174. - name: List the tables the bridge created in Postgres
  175. ansible.builtin.command:
  176. argv:
  177. - docker
  178. - exec
  179. - matrix-postgres-molecule
  180. - psql
  181. - --username={{ matrix_bridge_mautrix_telegram_database_username }}
  182. - --dbname={{ matrix_bridge_mautrix_telegram_database_name }}
  183. - --tuples-only
  184. - --no-align
  185. - "--command=SELECT tablename FROM pg_tables WHERE schemaname = 'public'"
  186. register: mautrix_telegram_tables
  187. changed_when: false
  188. - name: Assert the bridge migrated its schema into the configured database
  189. ansible.builtin.assert:
  190. that:
  191. - mautrix_telegram_tables.rc == 0
  192. - "'version' in mautrix_telegram_table_names"
  193. - mautrix_telegram_table_names | length > 5
  194. fail_msg: >-
  195. The bridge did not migrate its schema into
  196. {{ matrix_bridge_mautrix_telegram_database_name }}
  197. (found {{ mautrix_telegram_table_names | length }} table(s))
  198. success_msg: "The bridge migrated its schema into the configured database"
  199. vars:
  200. mautrix_telegram_table_names: "{{ mautrix_telegram_tables.stdout_lines | select | list }}"
  201. - name: Read the running container's image and user
  202. ansible.builtin.command:
  203. argv:
  204. - docker
  205. - container
  206. - inspect
  207. - matrix-mautrix-telegram
  208. - --format
  209. - "{{ '{{' }} .Config.Image {{ '}}' }} {{ '{{' }} .Config.User {{ '}}' }}"
  210. register: mautrix_telegram_container
  211. changed_when: false
  212. - name: Assert the running container uses the exact pinned image
  213. ansible.builtin.assert:
  214. that:
  215. - mautrix_telegram_container.stdout.split()[0] == 'dock.mau.dev/mautrix/telegram:' + mautrix_telegram_role_defaults.matrix_bridge_mautrix_telegram_version
  216. fail_msg: "The running container does not use the exact image the role pins"
  217. success_msg: "The running container uses the exact image the role pins"
  218. - name: Assert the running container uses the playbook-supplied identity
  219. ansible.builtin.assert:
  220. that:
  221. - mautrix_telegram_container.stdout.split()[1] == (matrix_user_uid | string) + ':' + (matrix_user_gid | string)
  222. fail_msg: "The running container does not use the {{ matrix_user_uid }}:{{ matrix_user_gid }} identity"
  223. success_msg: "The running container uses the playbook-supplied identity"
  224. - name: Read the labels the role rendered
  225. ansible.builtin.slurp:
  226. src: "{{ matrix_bridge_mautrix_telegram_base_path }}/labels"
  227. register: mautrix_telegram_labels
  228. - name: Assert the labels route the exposure hostname and prefix to the appservice port
  229. ansible.builtin.assert:
  230. that:
  231. - "'traefik.enable=true' in mautrix_telegram_labels_rendered"
  232. - "'traefik.http.services.matrix-mautrix-telegram-exposure.loadbalancer.server.port=8080' in mautrix_telegram_labels_rendered"
  233. - "'traefik.http.routers.matrix-mautrix-telegram-exposure.rule=Host(`bridges.molecule.local`) && PathPrefix(`/bridges/telegram`)' in mautrix_telegram_labels_rendered"
  234. - "'traefik.http.middlewares.matrix-mautrix-telegram-exposure-strip-prefix.stripprefix.prefixes=/bridges/telegram' in mautrix_telegram_labels_rendered"
  235. - "'traefik.docker.network=' + matrix_bridge_mautrix_telegram_container_network in mautrix_telegram_labels_rendered"
  236. fail_msg: "The labels do not route the exposure hostname and prefix to port 8080"
  237. success_msg: "The labels route the exposure hostname and prefix to port 8080"