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

244 строки
13 KiB

  1. # SPDX-FileCopyrightText: 2026 Slavi Pantaleev
  2. #
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. ---
  5. # What this proves: matrix-reminder-bot starts on the configuration the role
  6. # rendered, logs into a homeserver as the user the role gave it, opens the
  7. # database at the path the role gave it, and is the version the role pins.
  8. #
  9. # The bot has no HTTP surface of its own to probe, so the evidence is what it
  10. # says about itself in the journal plus what it left on disk. It does NOT set
  11. # real reminders and never will. See docs/molecule-testing.md.
  12. - name: Verify matrix-reminder-bot
  13. hosts: all
  14. become: true
  15. vars_files:
  16. - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/vars.yml"
  17. - "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/../../../molecule-shared/playbook-context.yml"
  18. gather_facts: false
  19. vars:
  20. matrix_bot_matrix_reminder_bot_molecule_user_id: "@{{ matrix_bot_matrix_reminder_bot_matrix_user_id_localpart }}:{{ matrix_domain }}"
  21. matrix_bot_matrix_reminder_bot_molecule_container_user: "{{ matrix_user_uid }}:{{ matrix_user_gid }}"
  22. tasks:
  23. # The version is read out of the role's own defaults rather than pinned in
  24. # molecule.yml, so that the assertion further down compares the running
  25. # image against what defaults/main.yml actually ships. Pinning it here
  26. # would make that assertion compare the scenario with itself.
  27. - name: Load the role's defaults under a separate name
  28. ansible.builtin.include_vars:
  29. file: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/main.yml"
  30. name: matrix_bot_matrix_reminder_bot_role_defaults
  31. - name: Wait for the matrix-reminder-bot service to become active
  32. ansible.builtin.systemd_service:
  33. name: matrix-bot-matrix-reminder-bot.service
  34. register: matrix_bot_matrix_reminder_bot_service
  35. until: matrix_bot_matrix_reminder_bot_service.status.ActiveState == 'active'
  36. retries: 30
  37. delay: 5
  38. failed_when: false
  39. # `Restart=always` means a bot crash-looping on a configuration it cannot
  40. # read still reports `active`, so the restart counter is checked too. The
  41. # config file is parsed before the bot's own catch-all retry loop starts, so
  42. # anything wrong in what the role rendered shows up here as restarts.
  43. # Asserted as `is defined` too, because `| int` turns a missing property
  44. # into 0 and would pass vacuously on a systemd that does not expose it.
  45. - name: Assert the service is active and has not been restarting
  46. ansible.builtin.assert:
  47. that:
  48. - matrix_bot_matrix_reminder_bot_service.status.ActiveState == 'active'
  49. - matrix_bot_matrix_reminder_bot_service.status.NRestarts is defined
  50. - matrix_bot_matrix_reminder_bot_service.status.NRestarts | int == 0
  51. fail_msg: >-
  52. matrix-bot-matrix-reminder-bot.service is
  53. {{ matrix_bot_matrix_reminder_bot_service.status.ActiveState | default('unknown') }}
  54. after {{ matrix_bot_matrix_reminder_bot_service.status.NRestarts | default('?') }}
  55. automatic restart(s)
  56. success_msg: "matrix-bot-matrix-reminder-bot.service is active and has not restarted"
  57. # The unit runs `docker start --attach`, so the container's output is in the
  58. # journal despite `--log-driver=none`. That is the only thing this bot
  59. # reports about itself - it serves nothing over HTTP.
  60. #
  61. # Filtered rather than tailed: the startup lines are the oldest ones in the
  62. # journal, so a `--lines=N` tail would lose them behind anything the bot
  63. # logs later, and reading the journal whole would pull an unbounded amount
  64. # of text into a variable. The filter keeps the failure line too, so the
  65. # "did not fail to log in" assertion below still has something to see.
  66. - name: Wait for the bot to report that it finished starting up
  67. ansible.builtin.shell:
  68. cmd: >-
  69. set -o pipefail;
  70. journalctl --unit=matrix-bot-matrix-reminder-bot.service --no-pager --output=cat --lines=all
  71. | grep -E 'Logged in as|Startup complete|Failed to login|Database initialization' | head -n 50 || true
  72. executable: /bin/bash
  73. register: matrix_bot_matrix_reminder_bot_journal
  74. changed_when: false
  75. until: "'Startup complete' in matrix_bot_matrix_reminder_bot_journal.stdout"
  76. retries: 24
  77. delay: 5
  78. failed_when: false
  79. # "Logged in as ..." is only reached after the bot's login call came back as
  80. # something other than a LoginError, so this is the whole chain at once: the
  81. # homeserver URL, the user ID and the password the role rendered were good
  82. # enough for a real login round-trip against the stub.
  83. - name: Assert the bot logged in as the user the role configured
  84. ansible.builtin.assert:
  85. that:
  86. - "'Startup complete' in matrix_bot_matrix_reminder_bot_journal.stdout"
  87. - "'Logged in as ' + matrix_bot_matrix_reminder_bot_molecule_user_id in matrix_bot_matrix_reminder_bot_journal.stdout"
  88. - "'Failed to login' not in matrix_bot_matrix_reminder_bot_journal.stdout"
  89. fail_msg: >-
  90. The bot did not log in as {{ matrix_bot_matrix_reminder_bot_molecule_user_id }}
  91. and reach startup
  92. success_msg: >-
  93. The bot logged in as {{ matrix_bot_matrix_reminder_bot_molecule_user_id }} and finished starting up
  94. # The role picks the storage engine (SQLite here, Postgres otherwise) by
  95. # building the connection string the bot parses, and the bot names the type
  96. # it settled on once the database is open.
  97. - name: Assert the bot opened the database engine the role selected
  98. ansible.builtin.assert:
  99. that:
  100. - "\"Database initialization of type 'sqlite' complete\" in matrix_bot_matrix_reminder_bot_journal.stdout"
  101. fail_msg: "The bot did not report a completed SQLite database initialization"
  102. success_msg: "The bot initialized the SQLite database the role pointed it at"
  103. - name: Read the configuration file the role rendered
  104. ansible.builtin.slurp:
  105. src: "{{ matrix_bot_matrix_reminder_bot_config_path }}/config.yaml"
  106. register: matrix_bot_matrix_reminder_bot_config_file
  107. # Every one of these differs from both the role's defaults and the bot's own
  108. # fallbacks, so their presence means the role rendered this file rather than
  109. # the values coinciding with what would have happened anyway.
  110. - name: Assert the rendered configuration carries this scenario's values
  111. ansible.builtin.assert:
  112. that:
  113. - matrix_bot_matrix_reminder_bot_molecule_user_id in matrix_bot_matrix_reminder_bot_config_rendered
  114. - matrix_bot_matrix_reminder_bot_matrix_user_password in matrix_bot_matrix_reminder_bot_config_rendered
  115. - matrix_bot_matrix_reminder_bot_matrix_homeserver_url in matrix_bot_matrix_reminder_bot_config_rendered
  116. - matrix_bot_matrix_reminder_bot_reminders_timezone in matrix_bot_matrix_reminder_bot_config_rendered
  117. - matrix_bot_matrix_reminder_bot_command_prefix in matrix_bot_matrix_reminder_bot_config_rendered
  118. - "'sqlite://' + matrix_bot_matrix_reminder_bot_sqlite_database_path_in_container in matrix_bot_matrix_reminder_bot_config_rendered"
  119. - "'@molecule-allowed:molecule.local' in matrix_bot_matrix_reminder_bot_config_rendered"
  120. - "'.*:blocked.molecule.local' in matrix_bot_matrix_reminder_bot_config_rendered"
  121. fail_msg: "The rendered configuration does not carry the scenario's settings"
  122. success_msg: "The rendered configuration carries the scenario's settings"
  123. vars:
  124. matrix_bot_matrix_reminder_bot_config_rendered: "{{ matrix_bot_matrix_reminder_bot_config_file.content | b64decode }}"
  125. # `device_name` is hardcoded in the role's config template, so this value can
  126. # only be there if `..._configuration_extension_yaml` was merged over the
  127. # template rather than ignored.
  128. - name: Assert the configuration extension was merged over the template
  129. ansible.builtin.assert:
  130. that:
  131. - "'device_name: Molecule Reminder Bot' in matrix_bot_matrix_reminder_bot_config_rendered"
  132. - "'device_name: Reminder Bot' not in matrix_bot_matrix_reminder_bot_config_rendered"
  133. fail_msg: >-
  134. The configuration extension did not override the device name the
  135. role's template hardcodes
  136. success_msg: "The configuration extension was merged over the role's template"
  137. vars:
  138. matrix_bot_matrix_reminder_bot_config_rendered: "{{ matrix_bot_matrix_reminder_bot_config_file.content | b64decode }}"
  139. # The bot has no HTTP surface, so where its database landed is the evidence
  140. # that the storage configuration reached the running process rather than
  141. # merely the file on disk.
  142. - name: Stat the database at the path the scenario configured
  143. ansible.builtin.stat:
  144. path: "{{ matrix_bot_matrix_reminder_bot_sqlite_database_path_local }}"
  145. register: matrix_bot_matrix_reminder_bot_database
  146. - name: Assert the database landed under the role's data path, owned by the role's user
  147. ansible.builtin.assert:
  148. that:
  149. - matrix_bot_matrix_reminder_bot_database.stat.exists
  150. - matrix_bot_matrix_reminder_bot_database.stat.uid == matrix_user_uid
  151. - matrix_bot_matrix_reminder_bot_database.stat.gid == matrix_user_gid
  152. fail_msg: >-
  153. {{ matrix_bot_matrix_reminder_bot_sqlite_database_path_local }} is missing or is not
  154. owned by {{ matrix_user_uid }}:{{ matrix_user_gid }}
  155. success_msg: >-
  156. The database is at the configured path, owned by {{ matrix_user_uid }}:{{ matrix_user_gid }}
  157. # A negative control for the assertion above: the role's own default
  158. # database name must NOT appear, or a file at the configured path would not
  159. # prove the configuration reached the process.
  160. - name: Stat the database name the role would have used by default
  161. ansible.builtin.stat:
  162. path: "{{ matrix_bot_matrix_reminder_bot_data_path }}/bot.db"
  163. register: matrix_bot_matrix_reminder_bot_default_database
  164. - name: Assert the role's default database name was not used
  165. ansible.builtin.assert:
  166. that:
  167. - not matrix_bot_matrix_reminder_bot_default_database.stat.exists
  168. fail_msg: >-
  169. {{ matrix_bot_matrix_reminder_bot_data_path }}/bot.db exists as well, so the
  170. database at the configured path does not prove the role's storage
  171. configuration reached the bot
  172. success_msg: "Only the configured database path was used"
  173. # matrix-nio writes its encryption store here once a login has succeeded, so
  174. # a populated directory means the bot could use the store path the role
  175. # created for it inside an otherwise read-only container.
  176. - name: List the encryption store the role created
  177. ansible.builtin.find:
  178. paths: "{{ matrix_bot_matrix_reminder_bot_data_store_path }}"
  179. file_type: file
  180. register: matrix_bot_matrix_reminder_bot_store_files
  181. - name: Assert the bot wrote its encryption store where the role put it
  182. ansible.builtin.assert:
  183. that:
  184. - matrix_bot_matrix_reminder_bot_store_files.matched | int > 0
  185. fail_msg: >-
  186. {{ matrix_bot_matrix_reminder_bot_data_store_path }} is empty, so the bot never
  187. got far enough to open its encryption store
  188. success_msg: "The bot wrote its encryption store under the role's data path"
  189. - name: Read the running container's user and environment
  190. ansible.builtin.command:
  191. argv:
  192. - docker
  193. - container
  194. - inspect
  195. - matrix-bot-matrix-reminder-bot
  196. - --format
  197. - "{{ '{{' }} .Config.User {{ '}}' }} {{ '{{' }} json .Config.Env {{ '}}' }} {{ '{{' }} .Config.Image {{ '}}' }}"
  198. register: matrix_bot_matrix_reminder_bot_container
  199. changed_when: false
  200. # The timezone reaches the container twice - through the config file checked
  201. # above and through TZ on the unit - and the uid/gid come from the playbook
  202. # context rather than from anything the image would pick on its own.
  203. - name: Assert the container runs as the role's user with the configured timezone
  204. ansible.builtin.assert:
  205. that:
  206. - "'\"TZ=' + matrix_bot_matrix_reminder_bot_reminders_timezone + '\"' in matrix_bot_matrix_reminder_bot_container.stdout"
  207. - matrix_bot_matrix_reminder_bot_molecule_container_user in matrix_bot_matrix_reminder_bot_container.stdout
  208. fail_msg: >-
  209. The container does not run as {{ matrix_user_uid }}:{{ matrix_user_gid }} with
  210. TZ={{ matrix_bot_matrix_reminder_bot_reminders_timezone }}
  211. ({{ matrix_bot_matrix_reminder_bot_container.stdout }})
  212. success_msg: >-
  213. The container runs as {{ matrix_user_uid }}:{{ matrix_user_gid }} with
  214. TZ={{ matrix_bot_matrix_reminder_bot_reminders_timezone }}
  215. - name: Assert the running container is the version defaults/main.yml pins
  216. ansible.builtin.assert:
  217. that:
  218. - matrix_bot_matrix_reminder_bot_role_defaults.matrix_bot_matrix_reminder_bot_version | string in matrix_bot_matrix_reminder_bot_container.stdout
  219. fail_msg: >-
  220. The running container is {{ matrix_bot_matrix_reminder_bot_container.stdout }}, which
  221. does not carry the pinned version
  222. {{ matrix_bot_matrix_reminder_bot_role_defaults.matrix_bot_matrix_reminder_bot_version }}
  223. success_msg: "The running container is the version defaults/main.yml pins"