The configuration template only rendered a datastore when its for_kinds list was non-empty, even though the inline documentation (matching upstream's) describes forKinds: [] as the way to make a datastore readonly. That made the documented migration scenario impossible: moving media between the file and s3 datastores while keeping the old one readable. Introduce matrix_media_repo_datastore_file_enabled and matrix_media_repo_datastore_s3_enabled, which default to the previous kinds-based behavior. A readonly datastore is now expressed by enabling the datastore explicitly while assigning it no kinds. Fixes #4303 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>master
| @@ -413,6 +413,14 @@ matrix_media_repo_shared_secret_auth_token: "PutSomeRandomSecureValueHere" | |||||
| # thumbnails and other misc data is also stored in these places. The media repo, when looking | # thumbnails and other misc data is also stored in these places. The media repo, when looking | ||||
| # for a datastore to use, will always use the smallest datastore first. | # for a datastore to use, will always use the smallest datastore first. | ||||
| # Controls whether the file datastore is defined in the configuration at all. | |||||
| # A datastore can be defined but disabled for uploads (making it readonly) by keeping this enabled | |||||
| # while setting `matrix_media_repo_datastore_file_for_kinds: []`. This is useful when migrating | |||||
| # media to another datastore, as previously stored media remains readable. | |||||
| # By default, the datastore is defined whenever some kinds are assigned to it, matching the | |||||
| # previous behavior of this role. | |||||
| matrix_media_repo_datastore_file_enabled: "{{ (matrix_media_repo_datastore_file_for_kinds | length) > 0 }}" | |||||
| # ID for the file datastore. Any unique alphanumeric string (e.g. generated via `pwgen -s 64 1`). | # ID for the file datastore. Any unique alphanumeric string (e.g. generated via `pwgen -s 64 1`). | ||||
| # This value CANNOT be changed after media has been stored — matrix-media-repo ties media to this ID. | # This value CANNOT be changed after media has been stored — matrix-media-repo ties media to this ID. | ||||
| matrix_media_repo_datastore_file_id: "" | matrix_media_repo_datastore_file_id: "" | ||||
| @@ -434,6 +442,14 @@ matrix_media_repo_datastore_file_for_kinds: ["thumbnails", "remote_media", "loca | |||||
| # Path to datastore, relative to matrix-media-repo directory root | # Path to datastore, relative to matrix-media-repo directory root | ||||
| matrix_media_repo_datastore_opts_path: "/data/media" | matrix_media_repo_datastore_opts_path: "/data/media" | ||||
| # Controls whether the S3 datastore is defined in the configuration at all. | |||||
| # A datastore can be defined but disabled for uploads (making it readonly) by keeping this enabled | |||||
| # while setting `matrix_media_repo_datastore_s3_for_kinds: []`. This is useful when migrating | |||||
| # media to another datastore, as previously stored media remains readable. | |||||
| # By default, the datastore is defined whenever some kinds are assigned to it, matching the | |||||
| # previous behavior of this role. | |||||
| matrix_media_repo_datastore_s3_enabled: "{{ (matrix_media_repo_datastore_s3_for_kinds | length) > 0 }}" | |||||
| # ID for the S3 datastore. Any unique alphanumeric string (e.g. generated via `pwgen -s 64 1`). | # ID for the S3 datastore. Any unique alphanumeric string (e.g. generated via `pwgen -s 64 1`). | ||||
| # This value CANNOT be changed after media has been stored — matrix-media-repo ties media to this ID. | # This value CANNOT be changed after media has been stored — matrix-media-repo ties media to this ID. | ||||
| matrix_media_repo_datastore_s3_id: "" | matrix_media_repo_datastore_s3_id: "" | ||||
| @@ -50,5 +50,5 @@ | |||||
| - {'name': 'matrix_media_repo_database_hostname', when: true} | - {'name': 'matrix_media_repo_database_hostname', when: true} | ||||
| - {'name': 'matrix_media_repo_container_labels_traefik_internal_media_entrypoints', when: "{{ matrix_media_repo_container_labels_traefik_internal_media_enabled }}"} | - {'name': 'matrix_media_repo_container_labels_traefik_internal_media_entrypoints', when: "{{ matrix_media_repo_container_labels_traefik_internal_media_enabled }}"} | ||||
| - {'name': 'matrix_media_repo_container_labels_traefik_internal_matrix_client_media_entrypoints', when: "{{ matrix_media_repo_container_labels_traefik_internal_matrix_client_media_enabled }}"} | - {'name': 'matrix_media_repo_container_labels_traefik_internal_matrix_client_media_entrypoints', when: "{{ matrix_media_repo_container_labels_traefik_internal_matrix_client_media_enabled }}"} | ||||
| - {'name': 'matrix_media_repo_datastore_file_id', when: "{{ (matrix_media_repo_datastore_file_for_kinds | length) > 0 }}"} | |||||
| - {'name': 'matrix_media_repo_datastore_s3_id', when: "{{ (matrix_media_repo_datastore_s3_for_kinds | length) > 0 }}"} | |||||
| - {'name': 'matrix_media_repo_datastore_file_id', when: "{{ matrix_media_repo_datastore_file_enabled }}"} | |||||
| - {'name': 'matrix_media_repo_datastore_s3_id', when: "{{ matrix_media_repo_datastore_s3_enabled }}"} | |||||
| @@ -198,7 +198,7 @@ sharedSecretAuth: | |||||
| # thumbnails and other misc data is also stored in these places. The media repo, when looking | # thumbnails and other misc data is also stored in these places. The media repo, when looking | ||||
| # for a datastore to use, will always use the smallest datastore first. | # for a datastore to use, will always use the smallest datastore first. | ||||
| datastores: | datastores: | ||||
| {% if (matrix_media_repo_datastore_file_for_kinds | length) > 0 %} | |||||
| {% if matrix_media_repo_datastore_file_enabled %} | |||||
| - type: file | - type: file | ||||
| # ID for this datastore (cannot change). Alphanumeric recommended. | # ID for this datastore (cannot change). Alphanumeric recommended. | ||||
| id: {{ matrix_media_repo_datastore_file_id | to_json }} | id: {{ matrix_media_repo_datastore_file_id | to_json }} | ||||
| @@ -218,7 +218,7 @@ datastores: | |||||
| opts: | opts: | ||||
| path: {{ matrix_media_repo_datastore_opts_path | to_json }} | path: {{ matrix_media_repo_datastore_opts_path | to_json }} | ||||
| {% endif %} | {% endif %} | ||||
| {% if (matrix_media_repo_datastore_s3_for_kinds | length) > 0 %} | |||||
| {% if matrix_media_repo_datastore_s3_enabled %} | |||||
| - type: s3 | - type: s3 | ||||
| # ID for this datastore (cannot change). Alphanumeric recommended. | # ID for this datastore (cannot change). Alphanumeric recommended. | ||||
| id: {{ matrix_media_repo_datastore_s3_id | to_json }} | id: {{ matrix_media_repo_datastore_s3_id | to_json }} | ||||