diff --git a/plugins/lookup/compose_file_args.py b/plugins/lookup/compose_file_args.py index e935d8f6c6..1f05a45e29 100644 --- a/plugins/lookup/compose_file_args.py +++ b/plugins/lookup/compose_file_args.py @@ -30,6 +30,7 @@ from ansible.errors import AnsibleError from ansible.plugins.loader import lookup_loader from ansible.plugins.lookup import LookupBase +from plugins.filter.role_path_by_app_id import abs_role_path_by_application_id from utils.docker.paths_utils import get_docker_paths from utils.templating.jinja import render_strict @@ -80,7 +81,7 @@ def _has_domain(domains: Any, application_id: str) -> bool: return _value_has_domain(domains) -def _role_provides_override(*, application_id: str, templar: Any) -> bool: +def _role_provides_override(*, application_id: str) -> bool: """ Mirror tasks/04_files.yml "with_first_found" logic: @@ -89,22 +90,10 @@ def _role_provides_override(*, application_id: str, templar: Any) -> bool: Only if one of these exists, we append "-f ". """ - tpl = getattr(templar, "template", None) - if not callable(tpl): - raise AnsibleError( - "compose_file_args: templar is required to resolve abs_role_path_by_application_id" - ) - - role_base = _as_str(tpl("{{ application_id | abs_role_path_by_application_id }}")) - if not role_base: - raise AnsibleError( - "compose_file_args: abs_role_path_by_application_id resolved to empty" - ) - - c1 = str(Path(role_base) / "templates" / "compose.override.yml.j2") - c2 = str(Path(role_base) / "files" / "compose.override.yml") - - return Path(c1).is_file() or Path(c2).is_file() + role_base = Path(abs_role_path_by_application_id(application_id)) + return (role_base / "templates" / "compose.override.yml.j2").is_file() or ( + role_base / "files" / "compose.override.yml" + ).is_file() class LookupModule(LookupBase): @@ -174,7 +163,7 @@ class LookupModule(LookupBase): parts = [f"{flag} {base}"] - if _role_provides_override(application_id=application_id, templar=templar): + if _role_provides_override(application_id=application_id): if not _as_str(override): raise AnsibleError( "compose_file_args: compose.files.compose_override is required " diff --git a/roles/web-app-bigbluebutton/README.md b/roles/web-app-bigbluebutton/README.md index b51b67a0ff..9a4911a4c2 100644 --- a/roles/web-app-bigbluebutton/README.md +++ b/roles/web-app-bigbluebutton/README.md @@ -48,8 +48,7 @@ flowchart LR svc_bbb_web["bbb-web"] svc_freeswitch["freeswitch"] svc_nginx["nginx"] - svc_etherpad["etherpad"] - svc_bbb_pads["bbb-pads"] + svc_bbb_shared_notes_server["bbb-shared-notes-server"] svc_bbb_export_annotations["bbb-export-annotations"] svc_webrtc_sfu["webrtc-sfu"] svc_fsesl_akka["fsesl-akka"] @@ -91,7 +90,7 @@ Solid `1:1` edges are fixed relationships; dashed `0..1` edges are conditional ( - ๐Ÿงฑ Automatic `.env` templating and domain/NGINX integration - ๐Ÿ›  Volume patching and Docker Compose customization - ๐Ÿ“ฌ SMTP integration and Greenlight admin creation -- ๐Ÿงช Workarounds for known Docker Compose or Etherpad issues +- ๐Ÿงช Workarounds for known Docker Compose issues ## Quick Setup diff --git a/roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js b/roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js deleted file mode 100644 index 47fcdd9c15..0000000000 --- a/roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js +++ /dev/null @@ -1,148 +0,0 @@ -// nocheck: mirrored-unit-test - requires ep_etherpad-lite's Settings module at load -// time and reads settings.ep_redis_publisher, so it cannot be imported outside an -// Etherpad install -const redis = require('redis'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); -const logger = { error: (...args) => console.error('[ep_redis_publisher]', ...args) }; - -const CHANNEL = settings.ep_redis_publisher.channel || 'from-etherpad-redis-channel'; - -const sanitizeMessageBody = (body) => { - let response = { ...body }; - - if (response.pad && response.pad._db) { - response = { - ...response, - pad: { - ...response.pad, - _db: undefined, - }, - }; - } - - if (body?.pad?.id) { body.pad.padId = body.pad.id; } - - return response; -}; - -const getCircularReplacer = () => { - const seen = new WeakSet(); - - return (key, value) => { - if (typeof value === "object" && value !== null) { - if (seen.has(value)) return; - - seen.add(value); - } - - return value; - }; -}; - -const retry = (options) => { - if (options.error && options.error.code === 'ECONNREFUSED') { - logger.error('redis', 'connection refused'); - - return new Error('refused'); - } - - if (options.total_retry_time > 1000 * 60 * 60) { - logger.error('redis', 'retry exhausted'); - - return new Error('exhausted'); - } - - if (options.attempt > 10) { - logger.error('redis', 'attempt limit'); - - return undefined; - } - - return Math.min(options.attempt * 100, 3000); -}; - -const options = { - host: settings.ep_redis_publisher.host || '127.0.0.1', - port: settings.ep_redis_publisher.port || 6379, - retry_strategy: retry, -}; - -if (settings.ep_redis_publisher.password) { - options.password = settings.ep_redis_publisher.password; -} - -const publisher = redis.createClient(options); - -const buildEnvelope = (name) => { - - return { - name, - routing: { sender: 'etherpad' }, - timestamp: (new Date()).getTime(), - }; -}; - -const buildCore = (name, body) => { - - return { - header: { name }, - body, - }; -}; - -const build = (name, body) => { - - return JSON.stringify({ - envelope: buildEnvelope(name), - core: buildCore(name, sanitizeMessageBody(body)), - }, getCircularReplacer()); -}; - -exports.publish = (event, body) => { - let message; - switch (event) { - case 'loadSettings': - message = build('PadLoadSettingsSysMsg', body); - break; - case 'shutdown': - message = build('PadShutdownSysMsg', body); - break; - case 'expressConfigure': - message = build('PadConfigureSysMsg', body); - break; - case 'expressCreateServer': - message = build('PadCreateServerSysMsg', body); - break; - case 'expressCloseServer': - message = build('PadCloseServerSysMsg', body); - break; - case 'onAccessCheck': - message = build('PadAccessCheckSysMsg', body); - break; - case 'padCreate': - message = build('PadCreateSysMsg', body); - break; - case 'padLoad': - message = build('PadLoadSysMsg', body); - break; - case 'padUpdate': - message = build('PadUpdateSysMsg', body); - break; - case 'padCopy': - message = build('PadCopySysMsg', body); - break; - case 'padRemove': - message = build('PadRemoveSysMsg', body); - break; - case 'userLeave': - message = build('PadUserLeaveSysMsg', body); - break; - - default: - return; - } - - if (message) { - publisher.publish(CHANNEL, message); - } -}; diff --git a/roles/web-app-bigbluebutton/files/shell/etherpad_pg_entrypoint.sh b/roles/web-app-bigbluebutton/files/shell/etherpad_pg_entrypoint.sh deleted file mode 100644 index 64ede8aef2..0000000000 --- a/roles/web-app-bigbluebutton/files/shell/etherpad_pg_entrypoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e - -sed -i \ - -e 's#"dbType": "redis"#"dbType": "postgres"#' \ - -e "s#\"url\": \"redis://redis:6379\"#\"host\": \"${ETHERPAD_DB_HOST}\", \"port\": ${ETHERPAD_DB_PORT}, \"database\": \"${ETHERPAD_DB_NAME}\", \"user\": \"${ETHERPAD_DB_USER}\", \"password\": \"${ETHERPAD_DB_PASSWORD}\"#" \ - /opt/etherpad-lite/settings.json - -exec /entrypoint.sh diff --git a/roles/web-app-bigbluebutton/files/shell/postgres_initdb_etherpad.sh b/roles/web-app-bigbluebutton/files/shell/postgres_initdb_etherpad.sh deleted file mode 100644 index 11364c0015..0000000000 --- a/roles/web-app-bigbluebutton/files/shell/postgres_initdb_etherpad.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -set -eu - -: "${POSTGRES_USER:?set by the upstream compose file}" -: "${ETHERPAD_DB_NAME:?set in env.j2 and broadcast to every service}" - -if ! psql --username "${POSTGRES_USER}" --dbname postgres -tAc \ - "SELECT 1 FROM pg_database WHERE datname = '${ETHERPAD_DB_NAME}'" | grep -q 1; then - psql --username "${POSTGRES_USER}" --dbname postgres \ - -c "CREATE DATABASE \"${ETHERPAD_DB_NAME}\"" -fi diff --git a/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py b/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py index 24d0413510..fb7e5a8514 100644 --- a/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py +++ b/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py @@ -3,7 +3,9 @@ import re from utils.cache.yaml import dump_yaml_str, load_yaml_str -def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): +def compose_mods( + yml_text, compose_repository_path, env_file, extra_hosts=None, pinned=None +): """Normalize the upstream-generated bigbluebutton compose file. Args: @@ -14,6 +16,9 @@ def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): env_file: env file broadcast to every service via ``env_file``. extra_hosts: optional host mappings added to every service that is not on the host network. + pinned: names of services whose image the role pins in + compose.override.yml; their upstream ``build`` is dropped even + where the upstream file names no image. Returns: Transformed compose YAML text. Structural deltas (named data @@ -32,7 +37,7 @@ def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): data = load_yaml_str(yml_text) or {} services = data.get("services", {}) or {} - for svc in services.values(): + for name, svc in services.items(): if not isinstance(svc, dict): continue @@ -49,7 +54,7 @@ def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): hosts.append(entry) svc["extra_hosts"] = hosts - if "build" in svc and svc.get("image"): + if "build" in svc and (svc.get("image") or name in (pinned or [])): svc.pop("build") data["services"] = services diff --git a/roles/web-app-bigbluebutton/meta/secrets.yml b/roles/web-app-bigbluebutton/meta/secrets.yml index 8a14f2b6f1..e3683a6e83 100644 --- a/roles/web-app-bigbluebutton/meta/secrets.yml +++ b/roles/web-app-bigbluebutton/meta/secrets.yml @@ -4,10 +4,6 @@ credentials: description: Shared secret for BigBlueButton API authentication algorithm: sha256 validation: ^[a-f0-9]{64}$ - etherpad_api_key: - description: API key for Etherpad integration - algorithm: random_hex_32 - validation: ^[a-zA-Z0-9]{32}$ rails_secret: description: Secret key for Rails backend algorithm: random_hex diff --git a/roles/web-app-bigbluebutton/meta/services.yml b/roles/web-app-bigbluebutton/meta/services.yml index e93411f153..b9a026c7b9 100644 --- a/roles/web-app-bigbluebutton/meta/services.yml +++ b/roles/web-app-bigbluebutton/meta/services.yml @@ -137,26 +137,13 @@ nginx: enabled: false # nocheck: playwright-service-flag: prebuilt upstream-compose image, mirror pre-pull only # nocheck: dynamic-flag: fixed upstream-compose sub-service, not group-gated -etherpad: - image: alangecker/bbb-docker-etherpad - version: "2.4.2-s8328b77-p88f3f6b" - update: - type: http_regex - url: - - https://raw.githubusercontent.com/bigbluebutton/docker/develop/repos/tags - - https://raw.githubusercontent.com/bigbluebutton/docker/develop/docker-compose.tmpl.yml - pattern: "repos/bbb-etherpad-plugin (?P[0-9a-f]+)\\nrepos/bbb-etherpad-skin (?P[0-9a-f]+)\\n(?:.*\\n)*?.*bbb-docker-etherpad:(?P[0-9.]+)-s" - template: "{base}-s{skin}-p{plugin}" - enabled: false -# nocheck: playwright-service-flag: prebuilt upstream-compose image, mirror pre-pull only -# nocheck: dynamic-flag: fixed upstream-compose sub-service, not group-gated -bbb-pads: - image: alangecker/bbb-docker-pads - version: "v1.5.8" +bbb-shared-notes-server: + image: alangecker/bbb-shared-notes-server + version: "v3.0.39" update: type: http_regex url: https://raw.githubusercontent.com/bigbluebutton/docker/develop/repos/tags - pattern: "repos/bbb-pads (v[0-9.]+)" + pattern: "repos/bigbluebutton (v[0-9.]+)" enabled: false # nocheck: playwright-service-flag: prebuilt upstream-compose image, mirror pre-pull only # nocheck: dynamic-flag: fixed upstream-compose sub-service, not group-gated diff --git a/roles/web-app-bigbluebutton/meta/volumes.yml b/roles/web-app-bigbluebutton/meta/volumes.yml index fc48d4e670..478bbded2f 100644 --- a/roles/web-app-bigbluebutton/meta/volumes.yml +++ b/roles/web-app-bigbluebutton/meta/volumes.yml @@ -62,25 +62,3 @@ coturn: mounts: - service: coturn target: /var/lib/coturn -ep_redis_publisher_patch: - type: bind - source: "{{ [lookup('container', 'web-app-bigbluebutton', 'directories.instance'), 'ep_redis_publisher_index.js'] | path_join }}" - read_only: true - mounts: - - service: etherpad - target: /opt/etherpad-lite/src/plugin_packages/.versions/ep_redis_publisher@0.0.3/index.js -etherpad_pg_entrypoint: - type: config - source: "{{ [lookup('container', 'web-app-bigbluebutton', 'directories.instance'), 'etherpad_pg_entrypoint.sh'] | path_join }}" - mode: "0444" - mounts: - - service: etherpad - target: /etherpad_pg_entrypoint.sh - -postgres_initdb_etherpad: - type: config - source: "{{ [lookup('container', 'web-app-bigbluebutton', 'directories.instance'), 'postgres_initdb_etherpad.sh'] | path_join }}" - mode: "0555" - mounts: - - service: postgres - target: /docker-entrypoint-initdb.d/zz-etherpad.sh diff --git a/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml b/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml index 24c5e4b44c..afb9259865 100644 --- a/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml +++ b/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml @@ -31,27 +31,6 @@ dest: "{{ [lookup('container', application_id, 'directories.instance'), 'compose.override.yml'] | path_join }}" notify: compose-up -- name: "๐Ÿฉน Vendor the ep_redis_publisher fix mounted over the etherpad plugin" - stack_host_copy: - src: javascript/ep_redis_publisher_index.js - dest: "{{ [lookup('container', application_id, 'directories.instance'), 'ep_redis_publisher_index.js'] | path_join }}" - mode: "0444" - notify: compose-up - -- name: "๐Ÿฉน Vendor the etherpad postgres-DB entrypoint wrapper" - stack_host_copy: - src: shell/etherpad_pg_entrypoint.sh - dest: "{{ [lookup('container', application_id, 'directories.instance'), 'etherpad_pg_entrypoint.sh'] | path_join }}" - mode: "0444" - notify: compose-up - -- name: "๐Ÿฉน Vendor the initdb hook that creates the etherpad database" - stack_host_copy: - src: shell/postgres_initdb_etherpad.sh - dest: "{{ [lookup('container', application_id, 'directories.instance'), 'postgres_initdb_etherpad.sh'] | path_join }}" - mode: "0555" - notify: compose-up - - name: ๐Ÿ“ Create symbolic link from .env file to target location file: src: "{{ BBB_ENV_FILE_ORIGINE }}" diff --git a/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml b/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml index 31542c38c4..58e6f60662 100644 --- a/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml +++ b/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml @@ -27,7 +27,8 @@ extra_hosts=( [lookup('domain', 'web-app-keycloak') ~ ':host-gateway'] if 'web-app-keycloak' in group_names else [] - ) + ), + pinned=BBB_PINNED_SERVICES ) }} dest: "{{ BBB_COMPOSE_FILE_FINAL }}" notify: diff --git a/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml b/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml index d36a6d7a0a..960638dd8f 100644 --- a/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml +++ b/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml @@ -30,26 +30,3 @@ delay: 3 until: bbb_pg_alter.rc == 0 changed_when: false - -- name: "๐Ÿ”Ž Check whether the etherpad database exists" - ansible.builtin.shell: > # nocheck: container-address-quote compose_mode_force role - container exec -i {{ BBB_POSTGRES_CONTAINER_ADDRESS | quote }} - psql -U postgres -d postgres -tAc - "SELECT 1 FROM pg_database WHERE datname = 'etherpad'" - register: bbb_etherpad_db - retries: 20 - delay: 3 - until: bbb_etherpad_db.rc == 0 - changed_when: false - -- name: "๐Ÿ—„๏ธ Create the etherpad database" - ansible.builtin.shell: > # nocheck: container-address-quote compose_mode_force role - container exec -i {{ BBB_POSTGRES_CONTAINER_ADDRESS | quote }} - psql -U postgres -d postgres -c "CREATE DATABASE etherpad" - register: bbb_etherpad_create - retries: 20 - delay: 3 - until: bbb_etherpad_create.rc == 0 or 'already exists' in bbb_etherpad_create.stderr - failed_when: bbb_etherpad_create.rc != 0 and 'already exists' not in bbb_etherpad_create.stderr - when: (bbb_etherpad_db.stdout | trim) != '1' - changed_when: bbb_etherpad_create.rc == 0 diff --git a/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 b/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 index 7d17058e94..fbcaaa9711 100644 --- a/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 +++ b/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 @@ -5,7 +5,7 @@ services: {{ lookup('container_volumes', service_name) | indent(4) }} {{ lookup('container_extra_hosts') | indent(4) }} {% if BBB_COLLABORA_ENABLED | bool %} - {{ lookup('depends_on', ['redis', 'etherpad', 'bbb-pads']) }} + {{ lookup('depends_on', ['redis']) }} {% endif %} {% set service_name = 'freeswitch' %} {{ service_name }}: @@ -15,19 +15,10 @@ services: {{ service_name }}: {{ lookup('container_image', application_id, service_name) }} {{ lookup('container_volumes', service_name) | indent(4) }} -{% set service_name = 'etherpad' %} - {{ service_name }}: - {{ lookup('container_image', application_id, service_name) }} - {{ lookup('container_volumes', service_name) | indent(4) }} - entrypoint: ["sh", "{{ lookup('volume', 'web-app-bigbluebutton', 'etherpad_pg_entrypoint').target }}"] -{% if BBB_COLLABORA_ENABLED | bool %} - {{ lookup('depends_on', ['redis', 'postgres']) }} -{% else %} - {{ lookup('depends_on', ['postgres']) }} -{% endif %} -{% set service_name = 'bbb-pads' %} +{% set service_name = 'bbb-shared-notes-server' %} {{ service_name }}: {{ lookup('container_image', application_id, service_name) }} + {{ lookup('compose_restart') }} {% set service_name = 'bbb-export-annotations' %} {{ service_name }}: {{ lookup('container_image', application_id, service_name) }} @@ -68,6 +59,8 @@ services: {% set service_name = 'greenlight' %} {{ service_name }}: {{ lookup('container_volumes', service_name) | indent(4) }} + environment: + URL_HOST: "{{ domain }}" {% endif %} {% if BBB_RECORDING_ENABLED | bool %} {% set service_name = 'recordings' %} diff --git a/roles/web-app-bigbluebutton/templates/env.j2 b/roles/web-app-bigbluebutton/templates/env.j2 index 73cbdfb7c6..9b99d0331b 100644 --- a/roles/web-app-bigbluebutton/templates/env.j2 +++ b/roles/web-app-bigbluebutton/templates/env.j2 @@ -23,12 +23,6 @@ REMOVE_OLD_RECORDING={{ BBB_RECORDING_CLEANUP_ENABLED | lower }} RECORDING_MAX_AGE_DAYS={{ BBB_RECORDING_MAX_AGE_DAYS }} SHARED_SECRET={{ BBB_SHARED_SECRET }} -ETHERPAD_API_KEY={{ BBB_ETHERPAD_API_KEY }} -ETHERPAD_DB_HOST=postgres -ETHERPAD_DB_PORT=5432 -ETHERPAD_DB_NAME=etherpad -ETHERPAD_DB_USER=postgres -ETHERPAD_DB_PASSWORD={{ BBB_POSTGRESQL_SECRET | dotenv_quote }} RAILS_SECRET={{ BBB_RAILS_SECRET }} POSTGRESQL_SECRET={{ BBB_POSTGRESQL_SECRET }} FSESL_PASSWORD={{ BBB_FSESL_PASSWORD | dotenv_quote }} diff --git a/roles/web-app-bigbluebutton/vars/main.yml b/roles/web-app-bigbluebutton/vars/main.yml index b7eafd9df5..3356e3faa2 100644 --- a/roles/web-app-bigbluebutton/vars/main.yml +++ b/roles/web-app-bigbluebutton/vars/main.yml @@ -9,11 +9,11 @@ http_port: "{{ lookup('config', application_id, 'services.bigbluebutton.ports.lo BBB_REPOSITORY: "{{ lookup('config', 'web-app-bigbluebutton', 'services.' ~ ('web-app-bigbluebutton' | get_entity_name) ~ '.repository') }}" BBB_REF: "{{ lookup('config', 'web-app-bigbluebutton', 'services.' ~ ('web-app-bigbluebutton' | get_entity_name) ~ '.ref') }}" +BBB_PINNED_SERVICES: "{{ lookup('config', application_id, 'services') | dict2items | selectattr('value.image', 'defined') | map(attribute='key') | list }}" _BBB_COTURN_ROLE: "web-svc-coturn" BBB_SHARED_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.shared_secret') }}" -BBB_ETHERPAD_API_KEY: "{{ lookup('config', application_id, 'secrets.credentials.etherpad_api_key') }}" BBB_RAILS_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.rails_secret') }}" BBB_POSTGRESQL_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.postgresql_secret') }}" BBB_FSESL_PASSWORD: "{{ lookup('config', application_id, 'secrets.credentials.fsesl_password') }}" diff --git a/tests/unit/python/plugins/lookup/test_compose_file_args.py b/tests/unit/python/plugins/lookup/test_compose_file_args.py index 8ad1a27a86..6d37f73d6b 100644 --- a/tests/unit/python/plugins/lookup/test_compose_file_args.py +++ b/tests/unit/python/plugins/lookup/test_compose_file_args.py @@ -1,5 +1,7 @@ import importlib.util +import tempfile import unittest +from pathlib import Path from unittest.mock import patch from ansible.errors import AnsibleError @@ -168,6 +170,16 @@ class TestComposeFArgs(unittest.TestCase): self.assertEqual(out, "-f /x/compose.yml") + def test_role_provides_override_reads_the_role_of_the_given_application(self): + with ( + tempfile.TemporaryDirectory() as role, + patch.object(self.m, "abs_role_path_by_application_id", return_value=role), + ): + self.assertFalse(self.m._role_provides_override(application_id="x")) + (Path(role) / "templates").mkdir() + (Path(role) / "templates" / "compose.override.yml.j2").write_text("") + self.assertTrue(self.m._role_provides_override(application_id="x")) + def test_requires_one_term(self): with self.assertRaises(AnsibleError): self.lookup.run([], variables=self.vars) diff --git a/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py b/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py index d5822c4e59..28084185c2 100644 --- a/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py +++ b/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py @@ -197,6 +197,26 @@ services: self.compose_repository_path + "/mod/html5-dev", ) + def test_build_stripped_for_a_pinned_service_without_upstream_image(self): + original = """services: + bbb-shared-notes-server: + build: + context: mod/bbb-shared-notes-server + html5-dev: + build: + context: mod/html5-dev +""" + services = load_yaml_str( + compose_mods( + original, + self.compose_repository_path, + self.env_file, + pinned=["bbb-shared-notes-server"], + ) + )["services"] + self.assertNotIn("build", services["bbb-shared-notes-server"]) + self.assertIn("build", services["html5-dev"]) + if __name__ == "__main__": unittest.main()